diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2010-04-12 23:45:32 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-04-12 23:52:15 -0400 |
commit | 4ca9c950dae7d81f96cf84b31f8242ec2718d9ed (patch) | |
tree | f317a8b09f5d02022999d6db4406c8e449bdb4b1 | |
parent | 1509d281169efda400f149126cce1f10a1920a48 (diff) |
Add "synthetic method" cache affinity loss experiment
Important things:
(commit 98d3dac1385deae671a106691294c150ddaf3f26 wip-bbb-cache-test)
- Allocate memory buffers from static allocation.
This avoids issues with the kernel giving us pre-warmed pages
from the LRU list.
-rw-r--r-- | SConstruct | 3 | ||||
-rw-r--r-- | bin/cache_cost.c | 322 |
2 files changed, 325 insertions, 0 deletions
@@ -201,6 +201,9 @@ pmpy = pmrt.Clone() | |||
201 | pmpy.Replace(LINKFLAGS = '') | 201 | pmpy.Replace(LINKFLAGS = '') |
202 | 202 | ||
203 | # ##################################################################### | 203 | # ##################################################################### |
204 | rt.Program('cache_cost', 'bin/cache_cost.c') | ||
205 | |||
206 | # ##################################################################### | ||
204 | # Preemption and migration overhead analysis | 207 | # Preemption and migration overhead analysis |
205 | 208 | ||
206 | pmrt.Program('pm_task', ['bin/pm_task.c', 'bin/pm_common.c']) | 209 | pmrt.Program('pm_task', ['bin/pm_task.c', 'bin/pm_common.c']) |
diff --git a/bin/cache_cost.c b/bin/cache_cost.c new file mode 100644 index 0000000..c41fa01 --- /dev/null +++ b/bin/cache_cost.c | |||
@@ -0,0 +1,322 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <time.h> | ||
4 | |||
5 | #include <signal.h> | ||
6 | #include <sys/mman.h> | ||
7 | #include <unistd.h> | ||
8 | #include <sched.h> | ||
9 | |||
10 | #include <sys/io.h> | ||
11 | #include <sys/utsname.h> | ||
12 | |||
13 | #include "litmus.h" | ||
14 | #include "asm.h" | ||
15 | |||
16 | static void die(char *error) | ||
17 | { | ||
18 | fprintf(stderr, "Error: %s (errno: %m)\n", | ||
19 | error); | ||
20 | exit(1); | ||
21 | } | ||
22 | |||
23 | static int check_migrations(int num_cpus) | ||
24 | { | ||
25 | int cpu, err; | ||
26 | |||
27 | for (cpu = 0; cpu < num_cpus; cpu++) { | ||
28 | err = be_migrate_to(cpu); | ||
29 | if (err != 0) { | ||
30 | fprintf(stderr, "Migration to CPU %d failed: %m.\n", | ||
31 | cpu + 1); | ||
32 | return 1; | ||
33 | } | ||
34 | } | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static int become_posix_realtime_task(void) | ||
39 | { | ||
40 | struct sched_param param; | ||
41 | |||
42 | param.sched_priority = sched_get_priority_max(SCHED_FIFO); | ||
43 | return sched_setscheduler(0 /* self */, SCHED_FIFO, ¶m); | ||
44 | } | ||
45 | |||
46 | /* must be larger than the largest cache in the system */ | ||
47 | #define ARENA_SIZE_MB 128 | ||
48 | #define INTS_IN_1KB (1024 / sizeof(int)) | ||
49 | #define ARENA_SIZE (INTS_IN_1KB * 1024 * ARENA_SIZE_MB) | ||
50 | static int page_idx = 0; | ||
51 | static int arena[ARENA_SIZE]; | ||
52 | |||
53 | static int lock_memory(void) | ||
54 | { | ||
55 | page_idx = getpagesize() / sizeof(int); | ||
56 | return mlockall(MCL_CURRENT | MCL_FUTURE); | ||
57 | } | ||
58 | |||
59 | static void touch_arena(void) { | ||
60 | int i; | ||
61 | for (i = 0; i < ARENA_SIZE; i++) | ||
62 | arena[i] = i; | ||
63 | } | ||
64 | |||
65 | static int* allocate(int wss) | ||
66 | { | ||
67 | static int pos = 0; | ||
68 | int size = wss * INTS_IN_1KB; | ||
69 | int *mem; | ||
70 | |||
71 | /* Don't allow re-use between allocations. | ||
72 | * At most half of the arena may be used | ||
73 | * at any one time. | ||
74 | */ | ||
75 | if (size * 2 > ARENA_SIZE) | ||
76 | die("static memory arena too small"); | ||
77 | |||
78 | if (pos + size > ARENA_SIZE) { | ||
79 | /* wrap to beginning */ | ||
80 | mem = arena; | ||
81 | pos = size; | ||
82 | } else { | ||
83 | mem = arena + pos; | ||
84 | pos += size; | ||
85 | } | ||
86 | |||
87 | return mem; | ||
88 | } | ||
89 | |||
90 | static void deallocate(int *mem) | ||
91 | { | ||
92 | } | ||
93 | |||
94 | static void migrate_to(int target) | ||
95 | { | ||
96 | if (be_migrate_to(target) != 0) | ||
97 | die("migration failed"); | ||
98 | } | ||
99 | |||
100 | static void sleep_us(int microseconds) | ||
101 | { | ||
102 | struct timespec delay; | ||
103 | |||
104 | delay.tv_sec = 0; | ||
105 | delay.tv_nsec = microseconds * 1000; | ||
106 | if (nanosleep(&delay, NULL) != 0) | ||
107 | die("sleep failed"); | ||
108 | } | ||
109 | |||
110 | static int touch_mem(int *mem, int wss, int write_cycle) | ||
111 | { | ||
112 | int sum = 0, i; | ||
113 | |||
114 | if (write_cycle > 0) { | ||
115 | for (i = 0; i < wss * 1024 / sizeof(int); i++) { | ||
116 | if (i % write_cycle == (write_cycle - 1)) | ||
117 | mem[i]++; | ||
118 | else | ||
119 | sum += mem[i]; | ||
120 | } | ||
121 | } else { | ||
122 | /* sequential access, pure read */ | ||
123 | for (i = 0; i < wss * 1024 / sizeof(int); i++) | ||
124 | sum += mem[i]; | ||
125 | } | ||
126 | return sum; | ||
127 | } | ||
128 | |||
129 | static void do_random_experiment(FILE* outfile, | ||
130 | int num_cpus, int wss, | ||
131 | int sleep_min, int sleep_max, | ||
132 | int write_cycle, int sample_count) | ||
133 | { | ||
134 | int last_cpu, next_cpu, delay; | ||
135 | unsigned long counter = 1; | ||
136 | |||
137 | cycles_t start, stop; | ||
138 | cycles_t cold, hot1, hot2, hot3, after_resume; | ||
139 | |||
140 | int *mem; | ||
141 | |||
142 | migrate_to(0); | ||
143 | last_cpu = 0; | ||
144 | |||
145 | /* prefault and dirty cache */ | ||
146 | touch_arena(); | ||
147 | |||
148 | |||
149 | iopl(3); | ||
150 | while (!sample_count || sample_count >= counter) { | ||
151 | delay = sleep_min + random() % (sleep_max - sleep_min + 1); | ||
152 | next_cpu = random() % num_cpus; | ||
153 | |||
154 | mem = allocate(wss); | ||
155 | |||
156 | cli(); | ||
157 | start = get_cycles(); | ||
158 | mem[0] = touch_mem(mem, wss, write_cycle); | ||
159 | stop = get_cycles(); | ||
160 | cold = stop - start; | ||
161 | |||
162 | start = get_cycles(); | ||
163 | mem[0] = touch_mem(mem, wss, write_cycle); | ||
164 | stop = get_cycles(); | ||
165 | hot1 = stop - start; | ||
166 | |||
167 | start = get_cycles(); | ||
168 | mem[0] = touch_mem(mem, wss, write_cycle); | ||
169 | stop = get_cycles(); | ||
170 | hot2 = stop - start; | ||
171 | |||
172 | start = get_cycles(); | ||
173 | mem[0] = touch_mem(mem, wss, write_cycle); | ||
174 | stop = get_cycles(); | ||
175 | hot3 = stop - start; | ||
176 | sti(); | ||
177 | |||
178 | migrate_to(next_cpu); | ||
179 | sleep_us(delay); | ||
180 | |||
181 | cli(); | ||
182 | start = get_cycles(); | ||
183 | mem[0] = touch_mem(mem, wss, write_cycle); | ||
184 | stop = get_cycles(); | ||
185 | sti(); | ||
186 | after_resume = stop - start; | ||
187 | |||
188 | /* run, write ratio, wss, delay, from, to, cold, hot1, hot2, | ||
189 | * hot3, after_resume */ | ||
190 | fprintf(outfile, | ||
191 | "%6ld, %3d, %6d, %6d, %3d, %3d, " | ||
192 | "%" CYCLES_FMT ", " | ||
193 | "%" CYCLES_FMT ", " | ||
194 | "%" CYCLES_FMT ", " | ||
195 | "%" CYCLES_FMT ", " | ||
196 | "%" CYCLES_FMT "\n", | ||
197 | counter++, write_cycle, | ||
198 | wss, delay, last_cpu, next_cpu, cold, hot1, hot2, hot3, | ||
199 | after_resume); | ||
200 | last_cpu = next_cpu; | ||
201 | deallocate(mem); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | static void on_sigalarm(int signo) | ||
206 | { | ||
207 | /*fprintf(stderr, "SIGALARM\n");*/ | ||
208 | exit(0); | ||
209 | } | ||
210 | |||
211 | static void usage(char *error) { | ||
212 | /* TODO: actually provide usage instructions */ | ||
213 | die(error); | ||
214 | } | ||
215 | |||
216 | |||
217 | #define OPTSTR "m:w:l:s:o:x:y:nc:" | ||
218 | |||
219 | int main(int argc, char** argv) | ||
220 | { | ||
221 | int num_cpus = 1; /* only do preemptions by default */ | ||
222 | int wss = 64; /* working set size, in kb */ | ||
223 | int sleep_min = 0; | ||
224 | int sleep_max = 1000; | ||
225 | int exit_after = 0; /* seconds */ | ||
226 | int write_cycle = 0; /* every nth cycle is a write; 0 means read-only */ | ||
227 | FILE* out = stdout; | ||
228 | char fname[255]; | ||
229 | struct utsname utsname; | ||
230 | int auto_name_file = 0; | ||
231 | int sample_count = 0; | ||
232 | int opt; | ||
233 | |||
234 | while ((opt = getopt(argc, argv, OPTSTR)) != -1) { | ||
235 | switch (opt) { | ||
236 | case 'm': | ||
237 | num_cpus = atoi(optarg); | ||
238 | break; | ||
239 | case 'c': | ||
240 | sample_count = atoi(optarg); | ||
241 | break; | ||
242 | case 's': | ||
243 | wss = atoi(optarg); | ||
244 | break; | ||
245 | case 'w': | ||
246 | write_cycle = atoi(optarg); | ||
247 | break; | ||
248 | case 'l': | ||
249 | exit_after = atoi(optarg); | ||
250 | break; | ||
251 | case 'o': | ||
252 | out = fopen(optarg, "w"); | ||
253 | if (out == NULL) | ||
254 | usage("could not open file"); | ||
255 | break; | ||
256 | case 'n': | ||
257 | auto_name_file = 1; | ||
258 | break; | ||
259 | case 'x': | ||
260 | sleep_min = atoi(optarg); | ||
261 | break; | ||
262 | case 'y': | ||
263 | sleep_max = atoi(optarg); | ||
264 | break; | ||
265 | case ':': | ||
266 | usage("Argument missing."); | ||
267 | break; | ||
268 | case '?': | ||
269 | default: | ||
270 | usage("Bad argument."); | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | if (num_cpus <= 0) | ||
276 | usage("Number of CPUs must be positive."); | ||
277 | |||
278 | if (wss <= 0) | ||
279 | usage("The working set size must be positive."); | ||
280 | |||
281 | if (sleep_min < 0 || sleep_min > sleep_max) | ||
282 | usage("Invalid minimum sleep time"); | ||
283 | |||
284 | if (write_cycle < 0) | ||
285 | usage("Write cycle may not be negative."); | ||
286 | |||
287 | if (sample_count < 0) | ||
288 | usage("Sample count may not be negative."); | ||
289 | |||
290 | if (check_migrations(num_cpus) != 0) | ||
291 | usage("Invalid CPU range."); | ||
292 | |||
293 | if (become_posix_realtime_task() != 0) | ||
294 | die("Could not become realt-time task."); | ||
295 | |||
296 | if (lock_memory() != 0) | ||
297 | die("Could not lock memory."); | ||
298 | |||
299 | if (auto_name_file) { | ||
300 | uname(&utsname); | ||
301 | snprintf(fname, 255, | ||
302 | "pmo_host=%s_wss=%d_wcycle=%d_smin=%d_smax=%d.csv", | ||
303 | utsname.nodename, wss, write_cycle, sleep_min, sleep_max); | ||
304 | out = fopen(fname, "w"); | ||
305 | if (out == NULL) { | ||
306 | fprintf(stderr, "Can't open %s.", fname); | ||
307 | die("I/O"); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | if (exit_after > 0) { | ||
312 | signal(SIGALRM, on_sigalarm); | ||
313 | alarm(exit_after); | ||
314 | } | ||
315 | |||
316 | do_random_experiment(out, | ||
317 | num_cpus, wss, sleep_min, | ||
318 | sleep_max, write_cycle, | ||
319 | sample_count); | ||
320 | fclose(out); | ||
321 | return 0; | ||
322 | } | ||