summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@cs.unc.edu>2010-04-12 23:45:32 -0400
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-04-12 23:52:15 -0400
commit4ca9c950dae7d81f96cf84b31f8242ec2718d9ed (patch)
treef317a8b09f5d02022999d6db4406c8e449bdb4b1
parent1509d281169efda400f149126cce1f10a1920a48 (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--SConstruct3
-rw-r--r--bin/cache_cost.c322
2 files changed, 325 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct
index fc3a24f..6f122cb 100644
--- a/SConstruct
+++ b/SConstruct
@@ -201,6 +201,9 @@ pmpy = pmrt.Clone()
201pmpy.Replace(LINKFLAGS = '') 201pmpy.Replace(LINKFLAGS = '')
202 202
203# ##################################################################### 203# #####################################################################
204rt.Program('cache_cost', 'bin/cache_cost.c')
205
206# #####################################################################
204# Preemption and migration overhead analysis 207# Preemption and migration overhead analysis
205 208
206pmrt.Program('pm_task', ['bin/pm_task.c', 'bin/pm_common.c']) 209pmrt.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
16static void die(char *error)
17{
18 fprintf(stderr, "Error: %s (errno: %m)\n",
19 error);
20 exit(1);
21}
22
23static 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
38static 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, &param);
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)
50static int page_idx = 0;
51static int arena[ARENA_SIZE];
52
53static int lock_memory(void)
54{
55 page_idx = getpagesize() / sizeof(int);
56 return mlockall(MCL_CURRENT | MCL_FUTURE);
57}
58
59static void touch_arena(void) {
60 int i;
61 for (i = 0; i < ARENA_SIZE; i++)
62 arena[i] = i;
63}
64
65static 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
90static void deallocate(int *mem)
91{
92}
93
94static void migrate_to(int target)
95{
96 if (be_migrate_to(target) != 0)
97 die("migration failed");
98}
99
100static 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
110static 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
129static 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
205static void on_sigalarm(int signo)
206{
207 /*fprintf(stderr, "SIGALARM\n");*/
208 exit(0);
209}
210
211static 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
219int 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}