diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-04-12 23:17:14 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-04-12 23:17:14 -0400 |
commit | 752ef77c1a70372bd2d2f7b349e2e25ac741c0c6 (patch) | |
tree | f5cdaa8e1f547a9ad8f71fd74d53ccc2931fb592 /bin/pm_task.c | |
parent | e1d76b979bace619aeaf4383f02b728ebb5558fd (diff) |
Add main pm test program
- pm_task and architectural dependent memory access
Diffstat (limited to 'bin/pm_task.c')
-rw-r--r-- | bin/pm_task.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/bin/pm_task.c b/bin/pm_task.c new file mode 100644 index 0000000..40f3fb9 --- /dev/null +++ b/bin/pm_task.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * pm_task.c | ||
3 | * | ||
4 | * A real-time task that measures preemption and migration costs | ||
5 | * for a specific working set size. | ||
6 | * | ||
7 | * 2008 Original version and idea by John Calandrino | ||
8 | * (litmus2008, liblitmus2008) | ||
9 | * | ||
10 | * 2010 Porting of original program to litmus2010 and | ||
11 | * integration within liblitmus2010 by Andrea Bastoni | ||
12 | */ | ||
13 | |||
14 | /* common data structures and defines */ | ||
15 | #include "pm_common.h" | ||
16 | |||
17 | #include "litmus.h" | ||
18 | #include "asm.h" | ||
19 | #include "cycles.h" | ||
20 | |||
21 | /* architectural dependend code for pm measurement */ | ||
22 | #include "pm_arch.h" | ||
23 | |||
24 | #include <sys/io.h> | ||
25 | |||
26 | int mem_block[NUMWS][INTS_PER_WSS] __attribute__ ((aligned(CACHEALIGNMENT))); | ||
27 | |||
28 | /* Setup flags, then enter loop to measure costs. */ | ||
29 | int main(int argc, char **argv) | ||
30 | { | ||
31 | /* control_page to read data from kernel */ | ||
32 | struct control_page *ctrl = NULL; | ||
33 | #ifdef DEBUG | ||
34 | struct control_page *saved_ctrl_page_ptr = 0; | ||
35 | #endif | ||
36 | |||
37 | unsigned long curr_job_count = 0; | ||
38 | unsigned long curr_sched_count = 0; | ||
39 | unsigned int curr_cpu = 0; | ||
40 | unsigned long curr_last_rt_task = 0; | ||
41 | unsigned long curr_ws = 0; | ||
42 | |||
43 | unsigned long long curr_preemption_length = 0; | ||
44 | |||
45 | unsigned long long start_time, end_time; | ||
46 | |||
47 | int *mem_ptr = NULL; | ||
48 | int *mem_ptr_end = NULL; | ||
49 | |||
50 | struct data_entry data_points[DATAPOINTS]; | ||
51 | int data_count = 0; | ||
52 | int data_wrapped = 0; | ||
53 | |||
54 | int refcount; | ||
55 | |||
56 | int task_pid = gettid(); | ||
57 | int task_period; | ||
58 | int read, *loc_ptr; | ||
59 | struct rt_task param; | ||
60 | |||
61 | char *filename; | ||
62 | #ifdef DEBUG | ||
63 | int i; | ||
64 | #endif | ||
65 | |||
66 | if (argc < 2) { | ||
67 | printf("pm_task: need a filename\n"); | ||
68 | return -1; | ||
69 | } | ||
70 | |||
71 | filename = argv[1]; | ||
72 | #ifdef DEBUG | ||
73 | fprintf(stderr, "Saving on %s\n",filename); | ||
74 | #endif | ||
75 | |||
76 | /* Initialize random library for read/write ratio enforcement. */ | ||
77 | srandom(SEEDVAL); | ||
78 | |||
79 | /* this will lock all pages and will call init_kernel_iface */ | ||
80 | init_litmus(); | ||
81 | |||
82 | /* Ensure that the pages that we care about, either because they | ||
83 | * are shared with the kernel or they are performance-critical, | ||
84 | * are loaded and locked in memory before benchmarking begins. | ||
85 | */ | ||
86 | memset(¶m, 0, sizeof(struct rt_task)); | ||
87 | |||
88 | memset(&mem_block, 0, sizeof(int) * NUMWS * INTS_PER_WSS); | ||
89 | |||
90 | memset(&mem_ptr, 0, sizeof(int*)); | ||
91 | memset(&mem_ptr_end, 0, sizeof(int*)); | ||
92 | |||
93 | /* Get task period. */ | ||
94 | if (get_rt_task_param(task_pid, ¶m) < 0) { | ||
95 | perror("Cannot get task parameters\n"); | ||
96 | return -1; | ||
97 | } | ||
98 | |||
99 | task_period = param.period / NS_PER_MS; | ||
100 | |||
101 | /* get the shared control page for this task */ | ||
102 | if (!(ctrl = get_ctrl_page())) { | ||
103 | perror("Cannot get the shared control page\n"); | ||
104 | return -1; | ||
105 | } | ||
106 | #ifdef DEBUG | ||
107 | saved_ctrl_page_ptr = ctrl; | ||
108 | #endif | ||
109 | |||
110 | /* Enter loop that measures preemption and migration costs. */ | ||
111 | while (curr_job_count * task_period < SIMRUNTIME) { | ||
112 | #ifdef DEBUG | ||
113 | /* try to understand if bad bad things happened */ | ||
114 | if(ctrl != saved_ctrl_page_ptr) { | ||
115 | fprintf(stderr, "BAD BAD BAD!\n\nCtrl page changed! %p != %p\n", | ||
116 | saved_ctrl_page_ptr, ctrl); | ||
117 | return -1; | ||
118 | } | ||
119 | #endif | ||
120 | if (curr_job_count != ctrl->job_count) { | ||
121 | |||
122 | /* ok, this is a new job. Get info from kernel */ | ||
123 | |||
124 | curr_job_count = ctrl->job_count; | ||
125 | curr_sched_count = ctrl->sched_count; | ||
126 | curr_cpu = ctrl->cpu; | ||
127 | curr_last_rt_task = ctrl->last_rt_task; | ||
128 | |||
129 | barrier(); | ||
130 | |||
131 | /* job's portion of the mem_block */ | ||
132 | curr_ws = curr_job_count % NUMWS; | ||
133 | |||
134 | mem_ptr = &mem_block[curr_ws][0]; | ||
135 | mem_ptr_end = mem_ptr + INTS_PER_WSS; | ||
136 | |||
137 | /* Access WS when cache cold, then immediately | ||
138 | * re-access to calculate "cache-hot" access time. | ||
139 | */ | ||
140 | |||
141 | /* Cache-cold accesses. */ | ||
142 | start_time = get_cycles(); | ||
143 | for (; mem_ptr < mem_ptr_end; mem_ptr += 1024) | ||
144 | readwrite_one_thousand_ints(mem_ptr); | ||
145 | end_time = get_cycles(); | ||
146 | |||
147 | data_points[data_count].timestamp = end_time; | ||
148 | |||
149 | /* Am I the same I was before? */ | ||
150 | if (curr_job_count != ctrl->job_count || | ||
151 | curr_sched_count != ctrl->sched_count || | ||
152 | curr_cpu != ctrl->cpu) | ||
153 | /* fishiness */ | ||
154 | data_points[data_count].access_type = 'c'; | ||
155 | else | ||
156 | /* okay */ | ||
157 | data_points[data_count].access_type = 'C'; | ||
158 | |||
159 | data_points[data_count].access_time = | ||
160 | end_time - start_time; | ||
161 | data_points[data_count].cpu = curr_cpu; | ||
162 | data_points[data_count].job_count = curr_job_count; | ||
163 | data_points[data_count].sched_count = curr_sched_count; | ||
164 | data_points[data_count].last_rt_task = curr_last_rt_task; | ||
165 | data_points[data_count].preemption_length = 0; | ||
166 | |||
167 | data_wrapped = ((data_count+1) / DATAPOINTS > 0); | ||
168 | data_count = (data_count+1) % DATAPOINTS; | ||
169 | |||
170 | barrier(); | ||
171 | |||
172 | /* "Best case". Read multiple times. */ | ||
173 | for (refcount = 0; refcount < REFTOTAL; refcount++) { | ||
174 | |||
175 | mem_ptr = &mem_block[curr_ws][0]; | ||
176 | |||
177 | start_time = get_cycles(); | ||
178 | for (; mem_ptr < mem_ptr_end; mem_ptr += 1024) | ||
179 | readwrite_one_thousand_ints(mem_ptr); | ||
180 | end_time = get_cycles(); | ||
181 | |||
182 | data_points[data_count].timestamp = end_time; | ||
183 | |||
184 | if (curr_job_count != ctrl->job_count || | ||
185 | curr_sched_count != ctrl->sched_count || | ||
186 | curr_cpu != ctrl->cpu) | ||
187 | /* fishiness */ | ||
188 | data_points[data_count]. | ||
189 | access_type = 'h'; | ||
190 | else | ||
191 | /* okay */ | ||
192 | data_points[data_count]. | ||
193 | access_type = 'H'; | ||
194 | |||
195 | data_points[data_count].access_time = | ||
196 | end_time - start_time; | ||
197 | data_points[data_count].cpu = curr_cpu; | ||
198 | data_points[data_count].job_count = | ||
199 | curr_job_count; | ||
200 | data_points[data_count].sched_count = | ||
201 | curr_sched_count; | ||
202 | data_points[data_count].last_rt_task = | ||
203 | curr_last_rt_task; | ||
204 | data_points[data_count].preemption_length = 0; | ||
205 | |||
206 | data_wrapped = | ||
207 | ((data_count+1) / DATAPOINTS > 0); | ||
208 | data_count = (data_count+1) % DATAPOINTS; | ||
209 | } | ||
210 | |||
211 | } else if (mem_ptr && mem_ptr_end && | ||
212 | (curr_sched_count != ctrl->sched_count || | ||
213 | curr_cpu != ctrl->cpu)) { | ||
214 | |||
215 | /* we have done at least one go in the "best case". | ||
216 | * job is the same => preempted / migrated | ||
217 | */ | ||
218 | curr_preemption_length = | ||
219 | ctrl->preempt_end - ctrl->preempt_start; | ||
220 | curr_job_count = ctrl->job_count; | ||
221 | curr_sched_count = ctrl->sched_count; | ||
222 | curr_cpu = ctrl->cpu; | ||
223 | curr_last_rt_task = ctrl->last_rt_task; | ||
224 | |||
225 | barrier(); | ||
226 | |||
227 | /* Measure preemption or migration cost. */ | ||
228 | mem_ptr = &mem_block[curr_ws][0]; | ||
229 | |||
230 | start_time = get_cycles(); | ||
231 | for (; mem_ptr < mem_ptr_end; mem_ptr += 1024) | ||
232 | readwrite_one_thousand_ints(mem_ptr); | ||
233 | end_time = get_cycles(); | ||
234 | |||
235 | data_points[data_count].timestamp = end_time; | ||
236 | |||
237 | /* just record pP, we tell the difference later */ | ||
238 | if (curr_job_count != ctrl->job_count || | ||
239 | curr_sched_count != ctrl->sched_count || | ||
240 | curr_cpu != ctrl->cpu) | ||
241 | /* fishiness */ | ||
242 | data_points[data_count].access_type = 'p'; | ||
243 | else | ||
244 | /* okay */ | ||
245 | data_points[data_count].access_type = 'P'; | ||
246 | |||
247 | data_points[data_count].access_time = | ||
248 | end_time - start_time; | ||
249 | data_points[data_count].cpu = curr_cpu; | ||
250 | data_points[data_count].job_count = curr_job_count; | ||
251 | data_points[data_count].sched_count = curr_sched_count; | ||
252 | data_points[data_count].last_rt_task = | ||
253 | curr_last_rt_task; | ||
254 | data_points[data_count].preemption_length = | ||
255 | curr_preemption_length; | ||
256 | |||
257 | data_wrapped = ((data_count+1) / DATAPOINTS > 0); | ||
258 | data_count = (data_count+1) % DATAPOINTS; | ||
259 | |||
260 | } else if (mem_ptr && mem_ptr_end) { | ||
261 | /* | ||
262 | * Ok, we run (and we wait for a p/m event): | ||
263 | * Read or write some random location in the WS | ||
264 | * to keep the task "cache warm". We only do | ||
265 | * this if the pointers are valid, because we | ||
266 | * do not want to skew the "cold" read of the WS | ||
267 | * on the first job. | ||
268 | */ | ||
269 | read = (random() % 100) < READRATIO; | ||
270 | loc_ptr = &mem_block[curr_ws][0]; | ||
271 | loc_ptr += (random() % INTS_PER_WSS); | ||
272 | |||
273 | barrier(); | ||
274 | |||
275 | if (read) | ||
276 | read_mem(loc_ptr); | ||
277 | else | ||
278 | write_mem(loc_ptr); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | #ifdef DEBUG | ||
283 | /* Print (most recent) results. */ | ||
284 | for (i = 0; i < (data_wrapped ? DATAPOINTS : data_count) ; i++) | ||
285 | fprintf(stderr, "(%c) - ACC %llu, CPU %u, PLEN %llu\n", | ||
286 | data_points[i].access_type, | ||
287 | data_points[i].access_time, data_points[i].cpu, | ||
288 | data_points[i].preemption_length); | ||
289 | #endif | ||
290 | serialize_data_entry(filename, data_points, | ||
291 | (data_wrapped ? DATAPOINTS : data_count)); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||