aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2013-10-15 20:19:57 -0400
committerGlenn Elliott <gelliott@cs.unc.edu>2013-10-15 20:19:57 -0400
commit311f3384882847edd83d688a1a2dcc12bdc59d23 (patch)
treef4a75065c57bfc29796c2eef349a02a97c6912d7
parente15736509ab36e33bc71a0fe1120f2974e389725 (diff)
CPMD and producer/consumer overhead scripts.
-rwxr-xr-xcache_cost.py181
-rwxr-xr-xdistill_pmo.py211
-rwxr-xr-xproduce_consume_cost.py172
-rw-r--r--utils/__init__.py0
-rwxr-xr-xutils/iqr.py32
-rw-r--r--utils/machines.py43
6 files changed, 639 insertions, 0 deletions
diff --git a/cache_cost.py b/cache_cost.py
new file mode 100755
index 0000000..9f4e54a
--- /dev/null
+++ b/cache_cost.py
@@ -0,0 +1,181 @@
1#!/usr/bin/python
2
3import os
4import copy
5import sys
6import string
7import smtplib
8import socket
9import time
10import itertools
11import multiprocessing
12
13from run.executable.executable import Executable
14
15test_mode = False
16email_notification = True
17
18def run_exp(nsamples, ncpu, numa_args, wss, wcycle, sleep_range_ms, walk, do_pollute, do_huge_pages, do_uncache_pages):
19
20 fstub = 'pmo'
21 fstub += '_host='+socket.gethostname().split('.')[0]
22 fstub += '_ncpu='+str(ncpu)
23 fstub += '_wss='+str(wss)
24 fstub += '_wcycle='+str(wcycle)
25 fstub += '_smin='+str(sleep_range_ms[0]*1000)
26 fstub += '_smax='+str(sleep_range_ms[1]*1000)
27 fstub += '_polluters='+str(do_pollute)
28 fstub += '_hpages='+str(do_huge_pages)
29 fstub += '_upages='+str(do_uncache_pages)
30 fstub += '_walk='+walk
31
32 filename = fstub + '.csv'
33
34 if os.path.exists(filename):
35 return
36
37 polluters = []
38 if do_pollute:
39 for i in range(multiprocessing.cpu_count()):
40 if numa_args:
41 # number of CPUs in one of two NUMA nodes
42 if ncpu == 6:
43 if i < 6:
44 numa_hack = ['--cpunodebind=0', '--interleave=0']
45 else:
46 numa_hack = ['--cpunodebind=1', '--interleave=1']
47 else:
48 numa_hack = ['--cpunodebind=0,1', '--interleave=0,1']
49 args = copy.deepcopy(numa_hack)
50 args.append('memthrash')
51 else:
52 args = []
53 args.append('-m')
54 args.append(str(i))
55 if numa_args:
56 polluters.append(Executable('numactl', args))
57 else:
58 polluters.append(Executable('memthrash', args))
59
60 if numa_args:
61 args = copy.deepcopy(numa_args)
62 args.append('cache_cost')
63 else:
64 args = []
65 args.append('-m')
66 args.append(str(ncpu))
67 args.append('-c')
68 args.append(str(nsamples))
69 args.append('-s')
70 args.append(str(wss))
71 args.append('-w')
72 args.append(str(wcycle))
73
74 if do_huge_pages:
75 args.append('-h')
76
77 if do_uncache_pages:
78 args.append('-u')
79
80 # cache_cost wants time in microseconds
81 args.append('-x')
82 args.append(str(sleep_range_ms[0]*1000))
83 args.append('-y')
84 args.append(str(sleep_range_ms[1]*1000))
85
86 if walk == 'rand':
87 args.append('-r')
88
89 args.append('-o')
90 args.append(filename)
91
92 if numa_args:
93 probe = Executable('numactl', args)
94 else:
95 probe = Executable('cache_cost', args)
96
97 if not test_mode:
98 if do_pollute:
99 for p in polluters:
100 p.execute()
101 time.sleep(10)
102
103 probe.execute()
104 probe.wait()
105
106 for p in polluters:
107 p.kill()
108 for p in polluters:
109 p.wait(False)
110 else:
111 print 'Process commands and arguments: '
112 if do_pollute:
113 for p in polluters:
114 print str(p)
115 print str(probe)
116
117def main(argv):
118 nsamples = 5000
119
120 # We may need to test different NUMA node configurations
121 # according to memory interleaving across the NUMA topology.
122 #
123 # For non-numa systems, do "<#cpu>: []"
124 # Ex., for a 4-CPU non-numa system: "4: []"
125 #
126 # NOTE: Must update NUMA hack in run_exp() for memthrash
127 # for your configuration.
128 #
129 # todo: configure numa args for system automatically
130 ncpu_and_numa_args = {
131 6: ['--cpunodebind=0', '--interleave=0']
132# 12: ['--cpunodebind=0,1', '--interleave=0,1']
133 }
134
135 wss_kb = [4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 3072, 4096, 8192, 12288, 16384]
136 write_cycle = [0, 64, 16, 4, 2, 1]
137
138 sleep_range_ms = [0,50]
139
140# uncache = [False, True]
141 uncache = [False]
142 huge_pages = [False]
143
144 pollute = [False, True]
145 walk = ['seq', 'rand']
146# walk = ['seq']
147
148 for ncpu, numa_args in ncpu_and_numa_args.iteritems():
149 for u in uncache:
150 for h in huge_pages:
151 if u == True and h == True:
152 # skip invalid combo
153 continue
154 for wcycle in write_cycle:
155 for w in walk:
156 for p in pollute:
157 for wss in wss_kb:
158 run_exp(nsamples, ncpu, numa_args, wss, wcycle, sleep_range_ms, w, p, h, u)
159
160 if email_notification:
161 _subject = "Cache Ovh Collection Complete!"
162 _to = "gelliott@cs.unc.edu"
163 _from = "gelliott@bonham.cs.unc.edu"
164 _text = "Cache Ovh Collection Complete!"
165 _body = string.join(("From: %s" % _from, "To: %s" % _to, "Subject: %s" % _subject, "", _text), "\r\n")
166 s = smtplib.SMTP("localhost")
167 s.sendmail(_from, [_to], _body)
168 s.quit()
169
170
171
172if __name__ == "__main__":
173
174 os.environ['PATH'] += ':../liblitmus'
175 os.environ['PATH'] += ':../cache-ovh'
176
177 if 'LD_LIBRARY_PATH' not in os.environ:
178 os.environ['LD_LIBRARY_PATH'] = ''
179 os.environ['LD_LIBRARY_PATH'] += ':../liblitmus'
180
181 main(sys.argv[1:])
diff --git a/distill_pmo.py b/distill_pmo.py
new file mode 100755
index 0000000..9821dd5
--- /dev/null
+++ b/distill_pmo.py
@@ -0,0 +1,211 @@
1#!/usr/bin/env python
2
3import os
4import re
5import fnmatch
6import shutil as sh
7import sys
8import csv
9import numpy as np
10from scipy.stats import scoreatpercentile
11import bisect
12from optparse import OptionParser
13
14from utils.machines import machines
15
16import utils.iqr
17
18class Topology:
19 ncpus, root, leaves, dist_mat = 0, None, None, None
20 levels = ['L1', 'L2', 'L3', 'Mem', 'System']
21
22 class Node:
23 idx, name, parent, children = 0, 'Unk', None, None
24 def __init__(self, idx, name, parent = None):
25 self.idx = idx
26 self.name = name
27 self.parent = parent
28 self.children = []
29 def __repr__(self):
30 return self.name + '_' + str(self.idx)
31
32 def __build_level_above(self, machine, l, child_nodes):
33 key = 'n' + l
34 if key in machine:
35 cluster_sz = machine[key]
36 else:
37 cluster_sz = 1
38 nchildren = len(child_nodes)
39 nodes = [self.Node(idx, l) for idx in range(nchildren/cluster_sz)]
40 for i in range(len(child_nodes)):
41 child_nodes[i].parent = nodes[i/cluster_sz]
42 nodes[i/cluster_sz].children.append(child_nodes[i])
43 return nodes
44
45 def __find_dist(self, a, b):
46 if a != b:
47 # pass-through (ex. as CPU is to private L1)
48 if len(a.parent.children) == 1:
49 return self.__find_dist(a.parent, b.parent)
50 else:
51 return 1 + self.__find_dist(a.parent, b.parent)
52 return 0
53
54 def __build_dist_matrix(self):
55 dist_mat = np.empty([self.ncpus, self.ncpus], int)
56 for i in range(self.ncpus):
57 for j in range(i, self.ncpus):
58 dist_mat[i,j] = dist_mat[j,i] = self.__find_dist(self.leaves[i], self.leaves[j])
59 return dist_mat
60
61 def __init__(self, machine):
62 self.ncpus = machine['sockets']*machine['cores_per_socket']
63
64 # build the Topology bottom up
65 self.leaves = [self.Node(idx, 'CPU') for idx in range(self.ncpus)]
66 nodes = self.leaves
67 for l in self.levels:
68 nodes = self.__build_level_above(machine, l, nodes)
69 self.root = nodes
70
71 self.dist_mat = self.__build_dist_matrix()
72
73
74 def __repr_level(self, node, stem, buf):
75 spacing = 3
76 buf += stem + node.name + '_' + str(node.idx) + '\n'
77 for c in node.children:
78 buf = self.__repr_level(c, stem + ' '*spacing, buf)
79 return buf
80
81 def __repr__(self):
82 buf = self.__repr_level(self.root[0], '', '')
83 return buf
84
85 def distance(self, a, b):
86 return self.dist_mat[a,b]
87
88
89topologies = {}
90def get_topo(host):
91 if host in topologies:
92 return topologies[host]
93 else:
94 topo = Topology(machines[host])
95 topologies[host] = topo
96 return topo
97
98# find the max/avg/std of preemption and migration
99def process_cpmd(csv_file, params):
100
101 if 'pmo' not in params:
102 raise Exception(('not cpmd overhead file: %s)') % csv_file)
103
104 topo = get_topo(params['host'])
105
106 print 'processing ' + csv_file
107
108 ifile = open(csv_file, "r")
109 reader = csv.reader(ifile)
110 costs = {}
111
112 SAMPLE = 0
113 WRITE_CYCLE = 1
114 WSS = 2
115 DELAY = 3
116 LAST_CPU = 4
117 NEXT_CPU = 5
118 DIST = 6
119 COLD = 7
120 HOT1 = 8
121 HOT2 = 9
122 HOT3 = 10
123 AFTER_RESUME = 11
124
125 for row in reader:
126 hot = min(int(row[HOT1]), int(row[HOT2]), int(row[HOT3]))
127 after = int(row[AFTER_RESUME])
128 cost = max(after - hot, 0)
129 distance = topo.distance(int(row[NEXT_CPU]), int(row[LAST_CPU]))
130 assert distance == int(row[DIST])
131 if distance not in costs:
132 costs[distance] = []
133 costs[distance].append(cost)
134
135 for d,c in costs.iteritems():
136 arr = np.array(c, float)
137 arr = np.sort(arr)
138 (arr, mincut, maxcut) = utils.iqr.apply_iqr(arr, 1.5)
139 for x in np.nditer(arr, op_flags=['readwrite']):
140 x[...] = utils.machines.cycles_to_us(params['host'], x)
141 costs[d] = arr
142
143 stats = {}
144# print costs
145 for d,arr in costs.iteritems():
146 stats[d] = {'max':arr.max(), 'median':np.median(arr), 'mean':arr.mean(), 'std':arr.std()}
147
148 return stats
149
150def parse_args():
151 parser = OptionParser("usage: %prog [files...]")
152 return parser.parse_args()
153
154def safe_split(t, delim):
155 t = t.split(delim)
156 if len(t) == 1:
157 t = tuple([t[0], None])
158 return t
159
160def get_level(machine, ncpus):
161 dist = get_topo(machine).distance(0, int(ncpus)-1)
162 names = ['L1', 'L2', 'L3', 'mem', 'sys']
163 if dist <= len(names):
164 return names[dist]
165 else:
166 raise Exception("Unable to determine level.")
167 return ''
168
169def main():
170 opts, args = parse_args()
171
172 files = filter(os.path.exists, args)
173
174 regex = fnmatch.translate("pmo_*.csv")
175 csvs = re.compile(regex)
176 files = filter(csvs.search, files)
177
178 results = {}
179 for f in files:
180 temp = os.path.basename(f).split(".csv")[0]
181 tokens = temp.split("_")
182
183 params = {k:v for (k,v) in map(lambda x: safe_split(x, "="), tokens)}
184 common = tuple([params['host'], params['ncpu'], params['wcycle'], params['polluters'], params['walk'], params['hpages'], params['upages']])
185 if common not in results:
186 results[common] = {}
187 results[common][int(params['wss'])] = process_cpmd(f, params)
188
189# print results
190 for common in results:
191 trends = results[common]
192 name = 'dpmo_host=%s_lvl=%s_wcycle=%s_polluters=%s_walk=%s_hpages=%s_upages=%s.csv' %
193 (common[0], get_level(common[0], common[1]), common[2], common[3], common[4], common[5], common[6])
194 f = open(name, 'w')
195 for w,stats in iter(sorted(trends.iteritems())):
196 f.write('%d' % w)
197 _mean = 0
198 _max = 0
199 for i,data in iter(sorted(stats.iteritems())):
200 dist_mean = data['mean']
201 _mean = max(_mean, dist_mean)
202 f.write(', %.6f' % dist_mean)
203 f.write(', %.6f' % _mean)
204 for i,data in iter(sorted(stats.iteritems())):
205 dist_max = data['max']
206 _max = max(_max, dist_max)
207 f.write(', %.6f' % dist_max)
208 f.write(', %.6f\n' % _max)
209
210if __name__ == '__main__':
211 main()
diff --git a/produce_consume_cost.py b/produce_consume_cost.py
new file mode 100755
index 0000000..fd08a8d
--- /dev/null
+++ b/produce_consume_cost.py
@@ -0,0 +1,172 @@
1#!/usr/bin/python
2
3import os
4import copy
5import sys
6import string
7import smtplib
8import socket
9import time
10import itertools
11import multiprocessing
12
13from run.executable.executable import Executable
14
15test_mode = False
16email_notification = True
17
18def run_exp(nsamples, ncpu, numa_args, wss, sleep_range_ms, walk, do_pollute, do_huge_pages, do_uncache_pages):
19
20 fstub = 'pco'
21 fstub += '_host='+socket.gethostname().split('.')[0]
22 fstub += '_ncpu='+str(ncpu)
23 fstub += '_wss='+str(wss)
24 fstub += '_smin='+str(sleep_range_ms[0]*1000)
25 fstub += '_smax='+str(sleep_range_ms[1]*1000)
26 fstub += '_polluters='+str(do_pollute)
27 fstub += '_hpages='+str(do_huge_pages)
28 fstub += '_upages='+str(do_uncache_pages)
29 fstub += '_walk='+walk
30
31 filename = fstub + '.csv'
32
33 if os.path.exists(filename):
34 return
35
36 polluters = []
37 if do_pollute:
38 for i in range(multiprocessing.cpu_count()):
39 if numa_args:
40 # number of CPUs in one of two NUMA nodes
41 if ncpu == 6:
42 if i < 6:
43 numa_hack = ['--cpunodebind=0', '--interleave=0']
44 else:
45 numa_hack = ['--cpunodebind=1', '--interleave=1']
46 else:
47 numa_hack = ['--cpunodebind=0,1', '--interleave=0,1']
48 args = copy.deepcopy(numa_hack)
49 args.append('memthrash')
50 else:
51 args = []
52 args.append('-m')
53 args.append(str(i))
54 if numa_args:
55 polluters.append(Executable('numactl', args))
56 else:
57 polluters.append(Executable('memthrash', args))
58
59 if numa_args:
60 args = copy.deepcopy(numa_args)
61 args.append('produce_consume_cost')
62 else:
63 args = []
64 args.append('-m')
65 args.append(str(ncpu))
66 args.append('-c')
67 args.append(str(nsamples))
68 args.append('-s')
69 args.append(str(wss))
70
71 if do_huge_pages:
72 args.append('-h')
73
74 if do_uncache_pages:
75 args.append('-u')
76
77 # cache_cost wants time in microseconds
78 args.append('-x')
79 args.append(str(sleep_range_ms[0]*1000))
80 args.append('-y')
81 args.append(str(sleep_range_ms[1]*1000))
82
83 if walk == 'rand':
84 args.append('-r')
85
86 args.append('-o')
87 args.append(filename)
88
89 if numa_args:
90 probe = Executable('numactl', args)
91 else:
92 probe = Executable('produce_consume_cost', args)
93
94 if not test_mode:
95 if do_pollute:
96 for p in polluters:
97 p.execute()
98 time.sleep(10)
99
100 probe.execute()
101 probe.wait()
102
103 for p in polluters:
104 p.kill()
105 for p in polluters:
106 p.wait(False)
107 else:
108 print 'Process commands and arguments: '
109 if do_pollute:
110 for p in polluters:
111 print str(p)
112 print str(probe)
113
114def main(argv):
115 nsamples = 5000
116
117 # We may need to test different NUMA node configurations
118 # according to memory interleaving across the NUMA topology.
119 #
120 # For non-numa systems, do "<#cpu>: []"
121 # Ex., for a 4-CPU non-numa system: "4: []"
122 #
123 # NOTE: Must update NUMA hack in run_exp() for memthrash
124 # for your configuration.
125 #
126 # todo: configure numa args for system automatically
127 ncpu_and_numa_args = {
128 6: ['--cpunodebind=0', '--interleave=0']
129# 12: ['--cpunodebind=0,1', '--interleave=0,1']
130 }
131
132 wss_kb = [4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 3072, 4096, 8192, 12288, 16384]
133 sleep_range_ms = [0,50]
134
135# uncache = [False, True]
136 uncache = [False]
137 huge_pages = [False]
138
139 pollute = [False, True]
140# walk = ['seq', 'rand']
141 walk = ['seq']
142
143 for ncpu, numa_args in ncpu_and_numa_args.iteritems():
144 for u in uncache:
145 for h in huge_pages:
146 for w in walk:
147 for p in pollute:
148 for wss in wss_kb:
149 run_exp(nsamples, ncpu, numa_args, wss, sleep_range_ms, w, p, h, u)
150
151 if email_notification:
152 _subject = "Producer/Consumer Ovh Collection Complete!"
153 _to = "gelliott@cs.unc.edu"
154 _from = "gelliott@bonham.cs.unc.edu"
155 _text = "Producer/Consumer Ovh Collection Complete!"
156 _body = string.join(("From: %s" % _from, "To: %s" % _to, "Subject: %s" % _subject, "", _text), "\r\n")
157 s = smtplib.SMTP("localhost")
158 s.sendmail(_from, [_to], _body)
159 s.quit()
160
161
162
163if __name__ == "__main__":
164
165 os.environ['PATH'] += ':../liblitmus'
166 os.environ['PATH'] += ':../cache-ovh'
167
168 if 'LD_LIBRARY_PATH' not in os.environ:
169 os.environ['LD_LIBRARY_PATH'] = ''
170 os.environ['LD_LIBRARY_PATH'] += ':../liblitmus'
171
172 main(sys.argv[1:])
diff --git a/utils/__init__.py b/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utils/__init__.py
diff --git a/utils/iqr.py b/utils/iqr.py
new file mode 100755
index 0000000..bbecdfa
--- /dev/null
+++ b/utils/iqr.py
@@ -0,0 +1,32 @@
1import numpy as np
2from scipy.stats import scoreatpercentile
3import bisect
4
5def find_lt(a, x):
6 i = bisect.bisect_left(a,x)
7 if i:
8 return i - 1
9 else:
10 return None
11
12def find_gt(a, x):
13 i = bisect.bisect_right(a, x)
14 if i != len(a):
15 return i
16 else:
17 return None
18
19def apply_iqr(seq, extent = 1.5):
20 q1 = scoreatpercentile(seq, 25)
21 q3 = scoreatpercentile(seq, 75)
22 iqr = q3 - q1
23 start = 0
24 end = len(seq) - 1
25 l = find_lt(seq, q1 - extent*iqr)
26 if l is not None:
27 start = l + 1
28 r = find_gt(seq, q3 + extent*iqr)
29 if r is not None:
30 end = r - 1
31 seq = seq[start:end+1]
32 return (seq, q1 - extent*iqr, q3 + extent*iqr)
diff --git a/utils/machines.py b/utils/machines.py
new file mode 100644
index 0000000..37a3c2f
--- /dev/null
+++ b/utils/machines.py
@@ -0,0 +1,43 @@
1machines = {
2 'ludwig': {
3 'cpu': 2133.0, # mhz
4 'sockets': 4,
5 'cores_per_socket': 6,
6 'L1': 256, # kb
7 'nL1': 1, # private
8 'L2': 3*1024,
9 'nL2': 2, # shared by two L1 modules
10 'L3': 12*1024,
11 'nL3': 3, # shared by three L2 modules
12 'nMem': 4 # shared by four L3 modules
13 },
14 'bonham': {
15 'cpu': 2666.0, # mhz
16 'sockets': 2,
17 'cores_per_socket': 6,
18 'L1': 32, # kb
19 'nL1': 1, # private
20 'L2': 2*1024,
21 'nL2': 1, # private
22 'L3': 12*1024,
23 'nL3': 6, # shared by six L2 modules
24 'nMem': 1 # shared by one L3 module (per mem)
25 },
26 'ringo': {
27 'cpu': 1400.0, # mhz
28 'sockets': 1,
29 'cores_per_socket': 4,
30 'L1': 32, # kb (just d-cache)
31 'nL1': 1, # private
32 'L2': 1024,
33 'nL2': 4, # shared by four cpus
34 'L3': 0, # n/a
35 'nL3': 0, # n/a
36 'nMem': 1
37 }
38}
39
40def cycles_to_us(machine_name, ncycles):
41 mhz = machines[machine_name]['cpu']
42 us = ncycles / mhz
43 return us