summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Bakita <jbakita@cs.unc.edu>2020-10-22 18:57:47 -0400
committerJoshua Bakita <jbakita@cs.unc.edu>2020-10-22 19:01:25 -0400
commit4f0a2f94f90c1d0cd5f3408a55e68b68a8c74693 (patch)
tree67e6d682d575c4f952f97add53f55e4e5daf9eb7
parente03d81c688e1070c16025c2a215ccb1a24ce2098 (diff)
Add script for computing M_i values
Remember, M_i values are the worst-case slowdown of the average- -case execution time. Also factor out functions shared with M_ij computation code to a shared library.
-rwxr-xr-xsmt_analysis/computeLCslowdown.py73
-rwxr-xr-xsmt_analysis/computeSMTslowdown.py154
-rwxr-xr-xsmt_analysis/libSMT.py151
3 files changed, 233 insertions, 145 deletions
diff --git a/smt_analysis/computeLCslowdown.py b/smt_analysis/computeLCslowdown.py
new file mode 100755
index 0000000..bcd22da
--- /dev/null
+++ b/smt_analysis/computeLCslowdown.py
@@ -0,0 +1,73 @@
1#!/usr/bin/python3
2import numpy as np
3import sys
4import plotille.plotille as plt
5from libSMT import *
6TIMING_ERROR = 1000 #ns
7ASYNC_FORMAT = False
8
9def print_usage():
10 print("This program takes in the all-pairs and baseline SMT data and computes the worst-case slowdown against any other task when SMT is enabled.", file=sys.stderr)
11 print("Level-A/B usage: {} <file -A> <file -B> <baseline file>".format(sys.argv[0]), file=sys.stderr)
12 print("Level-C usage: {} <continuous pairs> <baseline file>".format(sys.argv[0]), file=sys.stderr)
13
14# Check that we got the right number of parameters
15if len(sys.argv) < 3:
16 print_usage()
17 exit()
18
19if len(sys.argv) > 3:
20 print("Reading file using synchronous pair format...")
21 print("Are you sure you want to do this? For the RTAS'21 paper, L-A/-B pairs should use the other script.")
22 input("Press enter to continue, Ctrl+C to exit...")
23else:
24 print("Reading file using asynchronous pair format...")
25 ASYNC_FORMAT = True
26
27assert_valid_input_files(sys.argv[1:-1], print_usage)
28
29# Pull in the data
30if not ASYNC_FORMAT:
31 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[3])
32 paired_times, paired_offsets, name_to_idx, idx_to_name = load_paired(sys.argv[1], sys.argv[2], len(list(baseline_times.keys())))
33 for key in baseline_times:
34 print(key,max(baseline_times[key]))
35else:
36 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[2])
37 paired_times, name_to_idx, idx_to_name = load_fake_paired(sys.argv[1])
38
39# We work iff the baseline was run for the same set of benchmarks as the pairs were
40assert_base_and_pair_keys_match(baseline_times, name_to_idx)
41
42# Only consider benchmarks that are at least an order of magnitude longer than the timing error
43reliableNames = []
44for i in range(0, len(name_to_idx)):
45 benchmark = idx_to_name[i]
46 if min(baseline_times[benchmark]) > TIMING_ERROR * 10:
47 reliableNames.append(benchmark)
48
49# Compute worst-case SMT slowdown for each benchmark
50print("Bench Mi")
51# Print rows
52sample_f = np.mean # Change this to np.mean to use mean values in Mi generation
53M_vals = []
54for b1 in reliableNames:
55 print("{:<14.14}:".format(b1), end=" ")
56 max_mi = 0
57 # Scan through everyone we ran against and find our maximum slowdown
58 for b2 in reliableNames:
59 time_with_smt = sample_f(paired_times[name_to_idx[b1]][name_to_idx[b2]])
60 time_wout_smt = sample_f(baseline_times[b1])
61 M = time_with_smt / time_wout_smt
62 max_mi = max(max_mi, M)
63 print("{:>10.3}".format(max_mi), end=" ")
64 M_vals.append(max_mi)
65 print("")
66# Print some statistics about the distribution
67print("Average: {:>5.3} with standard deviation {:>5.3} using `{}`".format(np.mean(M_vals), np.std(M_vals), sample_f.__name__))
68Ms = np.asarray(M_vals, dtype=np.float32)
69print(np.sum(Ms <= 1), "of", len(M_vals), "M_i values are at most one -", 100*np.sum(Ms <= 1)/len(M_vals), "percent")
70print(np.sum(Ms > 2), "of", len(M_vals), "M_i values are greater than two -", 100*np.sum(Ms > 2)/len(M_vals), "percent")
71M_vals_to_plot = Ms
72
73print(plt.hist(M_vals_to_plot, bins=10))
diff --git a/smt_analysis/computeSMTslowdown.py b/smt_analysis/computeSMTslowdown.py
index aa7aa21..805def1 100755
--- a/smt_analysis/computeSMTslowdown.py
+++ b/smt_analysis/computeSMTslowdown.py
@@ -3,19 +3,19 @@ from typing import List, Any
3import numpy as np 3import numpy as np
4from scipy import stats 4from scipy import stats
5import sys 5import sys
6import os
7import plotille.plotille as plt 6import plotille.plotille as plt
8TIMING_ERROR = 1000 #ns 7TIMING_ERROR = 1000 #ns
9LEVEL_C_ANALYSIS = False 8LEVEL_C_ANALYSIS = False
9from libSMT import *
10 10
11def print_usage(argv): 11def print_usage():
12 print("This program takes in the all-pairs and baseline SMT data and computes how much each program is slowed when SMT in enabled.", file=sys.stderr) 12 print("This program takes in the all-pairs and baseline SMT data and computes how much each program is slowed when SMT in enabled.", file=sys.stderr)
13 print("Level-A/B usage: {} <file -A> <file -B> <baseline file> --cij".format(argv[0]), file=sys.stderr) 13 print("Level-A/B usage: {} <file -A> <file -B> <baseline file> --cij".format(sys.argv[0]), file=sys.stderr)
14 print("Level-C usage: {} <continuous pairs> <baseline file>".format(argv[0]), file=sys.stderr) 14 print("Level-C usage: {} <continuous pairs> <baseline file>".format(sys.argv[0]), file=sys.stderr)
15 15
16# Check that we got the right number of parameters 16# Check that we got the right number of parameters
17if len(sys.argv) < 3: 17if len(sys.argv) < 3:
18 print_usage(sys.argv) 18 print_usage()
19 exit() 19 exit()
20 20
21if len(sys.argv) > 3: 21if len(sys.argv) > 3:
@@ -24,127 +24,12 @@ else:
24 print("Analyzing results using Level-C methodology...") 24 print("Analyzing results using Level-C methodology...")
25 LEVEL_C_ANALYSIS = True 25 LEVEL_C_ANALYSIS = True
26 26
27# Check that all input files are valid 27assert_valid_input_files(sys.argv[1:-1], print_usage);
28for f in sys.argv[1:-1]:
29 if not os.path.exists(f) or os.path.getsize(f) == 0:
30 print("ERROR: File '{}' does not exist or is empty".format(f), file=sys.stderr);
31 print_usage(sys.argv)
32 exit()
33 28
34# Print Cij values rather than Mij 29# Print Cij values rather than Mij
35TIMES_ONLY = len(sys.argv) > 4 and "--cij" in sys.argv[4] 30TIMES_ONLY = len(sys.argv) > 4 and "--cij" in sys.argv[4]
36OK_PAIRS_ONLY = len(sys.argv) > 4 and "--cij-ok" in sys.argv[4] 31OK_PAIRS_ONLY = len(sys.argv) > 4 and "--cij-ok" in sys.argv[4]
37 32
38# This parses the result data from unthreaded timing experiments
39# @param f File name to load
40# @returns res Map of benchmark name to sample count
41# @returns samples Map of benchmark name to list of execution time samples
42# @returns max_res May of benchmark to maximum execution time among all samples for that benchmark
43def load_baseline(f):
44 # constants for columns of baseline data files
45 TOTAL_NS = 5
46 BENCH_NAME = 0
47 SAMPLES = 4
48
49 # Load baseline data. This logic is based off the summarize programs
50 res = {} # Map of benchmark to list of all execution time samples
51 samples = {} # Map of benchmark name to sample count
52 max_res = {} # Map of benchmark name to maximum execution time
53
54 with open(f) as fp:
55 for line in fp:
56 s = line.split()
57 if s[BENCH_NAME] not in res:
58 res[s[BENCH_NAME]] = list([int(s[TOTAL_NS])])
59 samples[s[BENCH_NAME]] = int(s[SAMPLES])
60 max_res[s[BENCH_NAME]] = int(s[TOTAL_NS])
61 else:
62 res[s[BENCH_NAME]].append(int(s[TOTAL_NS]))
63 max_res[s[BENCH_NAME]] = max(int(s[TOTAL_NS]), max_res[s[BENCH_NAME]])
64 return res, samples, max_res
65
66# This parses the result data from paired, threaded timing experiements
67# @param file1 The -A file name
68# @param file2 The -B file name
69# @returns time 2D array of benchmark IDs to list of total container execution times
70# @returns offset 2D array of benchmark IDs to list of differences between the start
71# of the first and the start of the second benchmark
72# @returns name_to_idx Map of benchmark names to benchmark IDs
73# @returns idx_to_name List which when indexed with benchmark ID will yield the benchmark name
74def load_paired(file1, file2, benchmarkCount):
75 # constants for columns of paired data files
76 FIRST_PROG = 0
77 SECOND_PROG = 1
78 FIRST_CORE = 2
79 SECOND_CORE = 3
80 TRIALS = 4
81 START_S = 5 # Start seconds
82 START_N = 6 # Start nanoseconds
83 END_S = 7 # End seconds
84 END_N = 8 # End nanoseconds
85 RUN_ID = 9
86 JOB_NUM = 10
87
88 with open(file1) as f1:
89 numJobs = int(f1.readline().split()[TRIALS])
90 assert numJobs > 0
91 assert benchmarkCount > 0
92
93 # Total times of each container
94 time=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
95 # Difference in time between when the first and the second task start in the container
96 offset=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
97
98 # Some aggregate counters that we update as we go along
99 avg_off = 0
100 avg_off_samp = 0
101
102 # Load paired data
103 bench1 = 0 # Index to what's the current first benchmark being examined
104 bench2 = 0 # Index to what's the current second benchmark being examined
105
106 name_to_idx = {}
107 idx_to_name = [0 for x in range(benchmarkCount)]
108
109 job_idx = 0
110 with open(file1) as f1, open(file2) as f2:
111 for line1, line2 in zip(f1, f2):
112 lineArr1 = line1.split()
113 lineArr2 = line2.split()
114 start1 = int(lineArr1[START_S]) * 10**9 + int(lineArr1[START_N])
115 start2 = int(lineArr2[START_S]) * 10**9 + int(lineArr2[START_N])
116 minStart = min(start1, start2)
117 end1 = int(lineArr1[END_S]) * 10**9 + int(lineArr1[END_N])
118 end2 = int(lineArr2[END_S]) * 10**9 + int(lineArr2[END_N])
119 maxEnd = max(end1, end2)
120 # Time actually co-scheduled is minEnd - maxStart, but Sims uses a different model
121# time[bench1][bench2][int(lineArr1[JOB_NUM])] = maxEnd - minStart
122 time[bench1][bench2][job_idx] = maxEnd - minStart
123 if lineArr1[SECOND_PROG] == "h264_dec" and lineArr2[JOB_NUM] == 0:
124 print(maxEnd - minStart)
125 # Compute offset: if first job starts at t=0, when does second start?
126# offset[bench1][bench2][int(lineArr1[JOB_NUM])] = abs(start2-start1)
127 offset[bench1][bench2][job_idx] = abs(start2-start1)
128 # Compute some running statistics
129 avg_off += abs(start2-start1)
130 avg_off_samp += 1
131 # Increment to the next benchmark, this is weird because of the zip()
132 # This is doubly weird because our results are an upper trianguler matrix
133 if job_idx == numJobs - 1: #int(lineArr1[JOB_NUM]) == numJobs - 1:
134 if bench2 < benchmarkCount-1:
135 bench2 = bench2 + 1
136 job_idx = 0
137 else:
138 name_to_idx[lineArr1[FIRST_PROG]] = bench1
139 idx_to_name[bench1] = lineArr1[FIRST_PROG]
140 bench1 = bench1 + 1
141 bench2 = bench1 # bench1 will never again appear as bench2
142 job_idx = 0
143 else:
144 job_idx += 1
145 print("Average offset is: " + str(avg_off/avg_off_samp) + "ns")
146 return time, offset, name_to_idx, idx_to_name
147
148# Pull in the data 33# Pull in the data
149if not LEVEL_C_ANALYSIS: 34if not LEVEL_C_ANALYSIS:
150 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[3]) 35 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[3])
@@ -152,33 +37,12 @@ if not LEVEL_C_ANALYSIS:
152 for key in baseline_times: 37 for key in baseline_times:
153 print(key,max(baseline_times[key])) 38 print(key,max(baseline_times[key]))
154else: 39else:
155 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[2])
156 # Paired times use an abuse of the baseline file format 40 # Paired times use an abuse of the baseline file format
157 paired_times_raw, _, _ = load_baseline(sys.argv[1]) 41 baseline_times, baseline_sample_cnt, baseline_max_times = load_baseline(sys.argv[2])
158 benchmarkCount = int(np.sqrt(len(list(paired_times_raw.keys())))) 42 paired_times, name_to_idx, idx_to_name = load_fake_paired(sys.argv[1])
159 numJobs = len(next(iter(paired_times_raw.values())))
160 paired_times=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
161 idx_to_name=[]
162 name_to_idx={}
163 bench1 = -1
164 #Generate the indexing approach
165 for pair in sorted(paired_times_raw.keys()):
166 [bench1name, bench2name] = pair.split('+') # Benchmark name is pair concatenated together with a '+' delimiter
167 if bench1 == -1 or bench1name != idx_to_name[-1]:
168 idx_to_name.append(bench1name)
169 name_to_idx[bench1name] = len(idx_to_name) - 1
170 bench1 += 1
171 # Populate the array
172 for bench1 in range(len(idx_to_name)):
173 for bench2 in range(len(idx_to_name)):
174 paired_times[bench1][bench2] = paired_times_raw[idx_to_name[bench1]+"+"+idx_to_name[bench2]]
175 43
176# We work iff the baseline was run for the same set of benchmarks as the pairs were 44# We work iff the baseline was run for the same set of benchmarks as the pairs were
177if sorted(baseline_times.keys()) != sorted(name_to_idx.keys()): 45assert_base_and_pair_keys_match(baseline_times, name_to_idx)
178 print("ERROR: The baseline and paired experiments were over a different set of benchmarks!", file=sys.stderr)
179 print("Baseline keys:", baseline_times.keys(), file=sys.stderr)
180 print("Paired keys:", name_to_idx.keys(), file=sys.stderr)
181 exit();
182 46
183# Only consider benchmarks that are at least an order of magnitude longer than the timing error 47# Only consider benchmarks that are at least an order of magnitude longer than the timing error
184reliableNames = [] 48reliableNames = []
diff --git a/smt_analysis/libSMT.py b/smt_analysis/libSMT.py
new file mode 100755
index 0000000..cca2fce
--- /dev/null
+++ b/smt_analysis/libSMT.py
@@ -0,0 +1,151 @@
1import numpy as np
2import sys
3import os
4
5def assert_valid_input_files(names, on_fail):
6 # Check that all input files are valid
7 for f in names:
8 if not os.path.exists(f) or os.path.getsize(f) == 0:
9 print("ERROR: File '{}' does not exist or is empty".format(f), file=sys.stderr);
10 on_fail()
11 exit()
12
13# This parses the result data from unthreaded timing experiments
14# @param f File name to load
15# @returns res Map of benchmark name to sample count
16# @returns samples Map of benchmark name to list of execution time samples
17# @returns max_res May of benchmark to maximum execution time among all samples for that benchmark
18def load_baseline(f):
19 # constants for columns of baseline data files
20 TOTAL_NS = 5
21 BENCH_NAME = 0
22 SAMPLES = 4
23
24 # Load baseline data. This logic is based off the summarize programs
25 res = {} # Map of benchmark to list of all execution time samples
26 samples = {} # Map of benchmark name to sample count
27 max_res = {} # Map of benchmark name to maximum execution time
28
29 with open(f) as fp:
30 for line in fp:
31 s = line.split()
32 if s[BENCH_NAME] not in res:
33 res[s[BENCH_NAME]] = list([int(s[TOTAL_NS])])
34 samples[s[BENCH_NAME]] = int(s[SAMPLES])
35 max_res[s[BENCH_NAME]] = int(s[TOTAL_NS])
36 else:
37 res[s[BENCH_NAME]].append(int(s[TOTAL_NS]))
38 max_res[s[BENCH_NAME]] = max(int(s[TOTAL_NS]), max_res[s[BENCH_NAME]])
39 return res, samples, max_res
40
41# This parses the result data from paired, threaded timing experiements
42# @param file1 The -A file name
43# @param file2 The -B file name
44# @returns time 2D array of benchmark IDs to list of total container execution times
45# @returns offset 2D array of benchmark IDs to list of differences between the start
46# of the first and the start of the second benchmark
47# @returns name_to_idx Map of benchmark names to benchmark IDs
48# @returns idx_to_name List which when indexed with benchmark ID will yield the benchmark name
49def load_paired(file1, file2, benchmarkCount):
50 # constants for columns of paired data files
51 FIRST_PROG = 0
52 SECOND_PROG = 1
53 FIRST_CORE = 2
54 SECOND_CORE = 3
55 TRIALS = 4
56 START_S = 5 # Start seconds
57 START_N = 6 # Start nanoseconds
58 END_S = 7 # End seconds
59 END_N = 8 # End nanoseconds
60 RUN_ID = 9
61 JOB_NUM = 10
62
63 with open(file1) as f1:
64 numJobs = int(f1.readline().split()[TRIALS])
65 assert numJobs > 0
66 assert benchmarkCount > 0
67
68 # Total times of each container
69 time=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
70 # Difference in time between when the first and the second task start in the container
71 offset=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
72
73 # Some aggregate counters that we update as we go along
74 avg_off = 0
75 avg_off_samp = 0
76
77 # Load paired data
78 bench1 = 0 # Index to what's the current first benchmark being examined
79 bench2 = 0 # Index to what's the current second benchmark being examined
80
81 name_to_idx = {}
82 idx_to_name = [0 for x in range(benchmarkCount)]
83
84 job_idx = 0
85 with open(file1) as f1, open(file2) as f2:
86 for line1, line2 in zip(f1, f2):
87 lineArr1 = line1.split()
88 lineArr2 = line2.split()
89 start1 = int(lineArr1[START_S]) * 10**9 + int(lineArr1[START_N])
90 start2 = int(lineArr2[START_S]) * 10**9 + int(lineArr2[START_N])
91 minStart = min(start1, start2)
92 end1 = int(lineArr1[END_S]) * 10**9 + int(lineArr1[END_N])
93 end2 = int(lineArr2[END_S]) * 10**9 + int(lineArr2[END_N])
94 maxEnd = max(end1, end2)
95 # Time actually co-scheduled is minEnd - maxStart, but Sims uses a different model
96# time[bench1][bench2][int(lineArr1[JOB_NUM])] = maxEnd - minStart
97 time[bench1][bench2][job_idx] = maxEnd - minStart
98 if lineArr1[SECOND_PROG] == "h264_dec" and lineArr2[JOB_NUM] == 0:
99 print(maxEnd - minStart)
100 # Compute offset: if first job starts at t=0, when does second start?
101# offset[bench1][bench2][int(lineArr1[JOB_NUM])] = abs(start2-start1)
102 offset[bench1][bench2][job_idx] = abs(start2-start1)
103 # Compute some running statistics
104 avg_off += abs(start2-start1)
105 avg_off_samp += 1
106 # Increment to the next benchmark, this is weird because of the zip()
107 # This is doubly weird because our results are an upper trianguler matrix
108 if job_idx == numJobs - 1: #int(lineArr1[JOB_NUM]) == numJobs - 1:
109 if bench2 < benchmarkCount-1:
110 bench2 = bench2 + 1
111 job_idx = 0
112 else:
113 name_to_idx[lineArr1[FIRST_PROG]] = bench1
114 idx_to_name[bench1] = lineArr1[FIRST_PROG]
115 bench1 = bench1 + 1
116 bench2 = bench1 # bench1 will never again appear as bench2
117 job_idx = 0
118 else:
119 job_idx += 1
120 print("Average offset is: " + str(avg_off/avg_off_samp) + "ns")
121 return time, offset, name_to_idx, idx_to_name
122
123# Paired times use an abuse of the baseline file format
124def load_fake_paired(fake_paired_filename):
125 paired_times_raw, _, _ = load_baseline(fake_paired_filename)
126 benchmarkCount = int(np.sqrt(len(list(paired_times_raw.keys()))))
127 numJobs = len(next(iter(paired_times_raw.values())))
128 paired_times=[[[0 for x in range(numJobs)]for y in range(benchmarkCount)]for z in range(benchmarkCount)]
129 idx_to_name=[]
130 name_to_idx={}
131 bench1 = -1
132 #Generate the indexing approach
133 for pair in sorted(paired_times_raw.keys()):
134 [bench1name, bench2name] = pair.split('+') # Benchmark name is pair concatenated together with a '+' delimiter
135 if bench1 == -1 or bench1name != idx_to_name[-1]:
136 idx_to_name.append(bench1name)
137 name_to_idx[bench1name] = len(idx_to_name) - 1
138 bench1 += 1
139 # Populate the array
140 for bench1 in range(len(idx_to_name)):
141 for bench2 in range(len(idx_to_name)):
142 paired_times[bench1][bench2] = paired_times_raw[idx_to_name[bench1]+"+"+idx_to_name[bench2]]
143 return paired_times, name_to_idx, idx_to_name
144
145def assert_base_and_pair_keys_match(baseline_times, name_to_idx):
146 if sorted(baseline_times.keys()) != sorted(name_to_idx.keys()):
147 print("ERROR: The baseline and paired experiments were over a different set of benchmarks!", file=sys.stderr)
148 print("Baseline keys:", baseline_times.keys(), file=sys.stderr)
149 print("Paired keys:", name_to_idx.keys(), file=sys.stderr)
150 exit();
151