diff options
Diffstat (limited to 'baseline/source')
-rw-r--r-- | baseline/source/extra.h | 275 |
1 files changed, 163 insertions, 112 deletions
diff --git a/baseline/source/extra.h b/baseline/source/extra.h index 02e97af..e8f3d18 100644 --- a/baseline/source/extra.h +++ b/baseline/source/extra.h | |||
@@ -53,45 +53,60 @@ extern int sched_getcpu(); | |||
53 | #define LOAD_PARAMS LOAD_PARAMS_ITRL | 53 | #define LOAD_PARAMS LOAD_PARAMS_ITRL |
54 | #endif | 54 | #endif |
55 | 55 | ||
56 | // Store state globally so that the job can be outside main() | ||
57 | // Arrays use float as a comprimise between overflow and size | ||
58 | float *_rt_exec_time; | ||
59 | #if MMDC_PERF | ||
60 | float *_rt_mmdc_read; | ||
61 | float *_rt_mmdc_write; | ||
62 | #endif | ||
63 | long _rt_jobs_complete; | ||
64 | long _rt_max_jobs; | ||
65 | int _rt_core; | ||
66 | int _rt_will_output; | ||
67 | struct timespec _rt_start, _rt_end; | ||
68 | |||
69 | char *_rt_run_id; | ||
70 | char *_rt_our_prog_name; | ||
71 | #define _RT_FILENAME_LEN 64 | ||
72 | |||
56 | #define LOAD_PARAMS_ITRL \ | 73 | #define LOAD_PARAMS_ITRL \ |
57 | if (argc != 6) { \ | 74 | if (argc != 6) { \ |
58 | fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);\ | 75 | fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);\ |
59 | fprintf(stderr, " <loops> integer number of iterations. -1 for infitite.\n");\ | 76 | fprintf(stderr, " <loops> integer number of iterations. -1 for infitite.\n");\ |
60 | fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");\ | 77 | fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");\ |
78 | fprintf(stderr, " <my core> UNUSED. Core is now auto-detected.\n");\ | ||
79 | exit(1);\ | ||
80 | }\ | ||
81 | _rt_our_prog_name = argv[1];\ | ||
82 | _rt_max_jobs = atol(argv[2]);\ | ||
83 | _rt_core = sched_getcpu();\ | ||
84 | _rt_run_id = argv[4];\ | ||
85 | _rt_will_output = atoi(argv[5]);\ | ||
86 | if (_rt_max_jobs < 0 && _rt_will_output != 0) {\ | ||
87 | fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n");\ | ||
61 | exit(1);\ | 88 | exit(1);\ |
62 | }\ | 89 | }\ |
63 | char *thisProgram=argv[1];\ | 90 | if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {\ |
64 | int parsedMaxJobs=atoi(argv[2]);\ | 91 | fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);\ |
65 | unsigned int thisCore=atoi(argv[3]);\ | ||
66 | thisCore = sched_getcpu();\ | ||
67 | char *runID=argv[4];\ | ||
68 | int output=atoi(argv[5]);\ | ||
69 | if (parsedMaxJobs < 0 && output != 0){\ | ||
70 | fprintf(stderr, "Infinite loops only supported when output is disabled!\n");\ | ||
71 | exit(1);\ | 92 | exit(1);\ |
72 | }\ | 93 | }\ |
73 | /* Cheat. -1 is larger than jobsComplete can ever reach. */\ | 94 | _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ |
74 | unsigned int maxJobs = parsedMaxJobs;\ | 95 | if (!_rt_exec_time) {\ |
75 | struct timespec _start, _end;\ | 96 | perror("Unable to allocate buffer for execution times");\ |
76 | int jobsComplete;\ | ||
77 | int jobs_complete = -1;\ | ||
78 | float *progTime = malloc(sizeof(float)*maxJobs*output);\ | ||
79 | memset(progTime, 0, sizeof(float)*maxJobs*output);\ | ||
80 | char fileName[64];\ | ||
81 | float *mmdc_read = malloc(sizeof(float)*maxJobs*output);\ | ||
82 | float *mmdc_write = malloc(sizeof(float)*maxJobs*output);\ | ||
83 | memset(mmdc_read, 0, sizeof(float)*maxJobs*output);\ | ||
84 | memset(mmdc_write, 0, sizeof(float)*maxJobs*output);\ | ||
85 | if (strlen(runID) + 5 > sizeof(fileName)) {\ | ||
86 | fprintf(stderr, "Run ID is too large! Keep it to less than 60 characters.\n");\ | ||
87 | exit(1);\ | 97 | exit(1);\ |
88 | }\ | 98 | }\ |
89 | strcpy(fileName, runID);\ | 99 | _rt_jobs_complete = 0;\ |
90 | strcat(fileName, ".txt");\ | ||
91 | mlockall(MCL_CURRENT || MCL_FUTURE); | 100 | mlockall(MCL_CURRENT || MCL_FUTURE); |
92 | 101 | ||
93 | #define SETUP_MMDC \ | 102 | #define SETUP_MMDC \ |
94 | MMDC_PROFILE_RES_t mmdc_res; \ | 103 | _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ |
104 | _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ | ||
105 | if (!_rt_mmdc_read || !_rt_mmdc_write) {\ | ||
106 | perror("Unable to allocate buffer for MMDC data");\ | ||
107 | exit(1);\ | ||
108 | }\ | ||
109 | MMDC_PROFILE_RES_t mmdc_res;\ | ||
95 | memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\ | 110 | memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\ |
96 | int fd = open("/dev/mem", O_RDWR, 0);\ | 111 | int fd = open("/dev/mem", O_RDWR, 0);\ |
97 | if (fd < 0) {\ | 112 | if (fd < 0) {\ |
@@ -107,9 +122,8 @@ extern int sched_getcpu(); | |||
107 | msync(&(mmdc->madpcr1),4,MS_SYNC); | 122 | msync(&(mmdc->madpcr1),4,MS_SYNC); |
108 | 123 | ||
109 | #define SETUP_LITMUS \ | 124 | #define SETUP_LITMUS \ |
110 | unsigned int cpu = atoi(thisCore); \ | ||
111 | unsigned int wait = 0; \ | 125 | unsigned int wait = 0; \ |
112 | if (be_migrate_to_domain(cpu) < 0) { \ | 126 | if (be_migrate_to_domain(_rt_core) < 0) { \ |
113 | perror("Unable to migrate to specified CPU"); \ | 127 | perror("Unable to migrate to specified CPU"); \ |
114 | exit(1); \ | 128 | exit(1); \ |
115 | } \ | 129 | } \ |
@@ -176,54 +190,6 @@ extern int sched_getcpu(); | |||
176 | } \ | 190 | } \ |
177 | reservation_destroy(gettid(), rt_param.cpu); | 191 | reservation_destroy(gettid(), rt_param.cpu); |
178 | 192 | ||
179 | #if MMDC_PROF | ||
180 | #define SAVE_RESULTS \ | ||
181 | if(jobs_complete >= maxJobs) {\ | ||
182 | fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ | ||
183 | exit(1);\ | ||
184 | }\ | ||
185 | if(jobs_complete > -1 && output) {\ | ||
186 | progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ | ||
187 | progTime[jobs_complete] *= 1000000000;\ | ||
188 | progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ | ||
189 | mmdc_read[jobs_complete] = mmdc_res.read_bytes;\ | ||
190 | mmdc_write[jobs_complete] = mmdc_res.write_bytes;\ | ||
191 | } | ||
192 | #else | ||
193 | #define SAVE_RESULTS \ | ||
194 | if(jobs_complete >= maxJobs) {\ | ||
195 | fprintf(stderr, "Max jobs setting too small! Trying to record job #%d when we only have space for %d jobs. Exiting...\n", jobs_complete, maxJobs);\ | ||
196 | exit(1);\ | ||
197 | }\ | ||
198 | if(jobs_complete > -1 && output) {\ | ||
199 | progTime[jobs_complete] = _end.tv_sec - _start.tv_sec;\ | ||
200 | progTime[jobs_complete] *= 1000000000;\ | ||
201 | progTime[jobs_complete] += _end.tv_nsec - _start.tv_nsec;\ | ||
202 | } | ||
203 | #endif | ||
204 | |||
205 | |||
206 | #if LITMUS | ||
207 | #define WRITE_TO_FILE WRITE_TO_FILE_ITRNL CLEANUP_LITMUS | ||
208 | #else | ||
209 | #define WRITE_TO_FILE WRITE_TO_FILE_ITRNL | ||
210 | #endif | ||
211 | |||
212 | #define WRITE_TO_FILE_ITRNL if (output){\ | ||
213 | munlockall();\ | ||
214 | FILE *fp=fopen(fileName, "a");\ | ||
215 | if (fp == NULL) {\ | ||
216 | perror("Unable to open output file");\ | ||
217 | exit(1);\ | ||
218 | }\ | ||
219 | for (int i = 0; i <= jobs_complete; i++){\ | ||
220 | fprintf(fp, "%s none %u none %d %.f %s %d %.f %.f \n",\ | ||
221 | thisProgram, thisCore, maxJobs,\ | ||
222 | progTime[i], runID, i, mmdc_read[i], mmdc_write[i]);\ | ||
223 | }\ | ||
224 | fclose(fp);\ | ||
225 | } | ||
226 | |||
227 | #if __arm__ | 193 | #if __arm__ |
228 | // On ARM, manually flush the cache | 194 | // On ARM, manually flush the cache |
229 | #define FLUSH_CACHES \ | 195 | #define FLUSH_CACHES \ |
@@ -247,53 +213,138 @@ extern int sched_getcpu(); | |||
247 | fclose(fp); | 213 | fclose(fp); |
248 | #endif | 214 | #endif |
249 | 215 | ||
216 | // Buffer timing result from a single job | ||
217 | static void _rt_save_job_result() { | ||
218 | if (_rt_jobs_complete >= _rt_max_jobs) { | ||
219 | fprintf(stderr, "Max jobs setting too small! Trying to record job #%ld when we only have space for %ld jobs. Exiting...\n", _rt_jobs_complete, _rt_max_jobs); | ||
220 | exit(1); | ||
221 | } | ||
222 | if (_rt_jobs_complete > -1 && _rt_will_output) { | ||
223 | _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec; | ||
224 | _rt_exec_time[_rt_jobs_complete] *= 1e9; | ||
225 | _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec; | ||
250 | #if MMDC_PROF | 226 | #if MMDC_PROF |
251 | #define START_TIMER \ | 227 | _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes; |
252 | /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ \ | 228 | _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes; |
253 | start_mmdc_profiling(mmdc); \ | ||
254 | /*nanosleep(&(struct timespec){0, ms2ns(999)}, NULL);*/ \ | ||
255 | clock_gettime(CLOCK_MONOTONIC, &_start); | ||
256 | #else | ||
257 | #define START_TIMER clock_gettime(CLOCK_MONOTONIC, &_start); | ||
258 | #endif | 229 | #endif |
230 | } | ||
231 | } | ||
259 | 232 | ||
233 | // Save all buffered timing results to disk | ||
234 | static void _rt_write_to_file() { | ||
235 | char fileName[_RT_FILENAME_LEN]; | ||
236 | FILE *fp; | ||
237 | if (!_rt_will_output) | ||
238 | return; | ||
239 | munlockall(); | ||
240 | strcpy(fileName, _rt_run_id); | ||
241 | strcat(fileName, ".txt"); | ||
242 | fp = fopen(fileName, "a"); | ||
243 | if (fp == NULL) { | ||
244 | perror("Unable to open _rt_will_output file"); | ||
245 | exit(1); | ||
246 | } | ||
247 | // Same format as the paired results with "none" for unused fields | ||
248 | for (int i = 0; i < _rt_jobs_complete; i++){ | ||
249 | fprintf(fp, "%s none %u none %ld %.f %s %d %.f %.f \n", | ||
250 | _rt_our_prog_name, _rt_core, _rt_max_jobs, | ||
251 | _rt_exec_time[i], _rt_run_id, i, | ||
260 | #if MMDC_PROF | 252 | #if MMDC_PROF |
261 | #define STOP_TIMER \ | 253 | _rt_mmdc_read[i], _rt_mmdc_write[i]); |
262 | clock_gettime(CLOCK_MONOTONIC, &_end); \ | ||
263 | /* This freezes the profiling and makes results available */ \ | ||
264 | pause_mmdc_profiling(mmdc); \ | ||
265 | get_mmdc_profiling_results(mmdc, &mmdc_res); | ||
266 | #else | 254 | #else |
267 | #define STOP_TIMER clock_gettime(CLOCK_MONOTONIC, &_end); | 255 | 0.0, 0.0); |
268 | #endif | 256 | #endif |
257 | } | ||
258 | fclose(fp); | ||
259 | #if LITMUS | ||
260 | CLEANUP_LITMUS | ||
261 | #endif | ||
262 | } | ||
269 | 263 | ||
270 | #define SLEEP nanosleep((const struct timespec[]){{0, 1000000}}, NULL); | 264 | // Start a job |
271 | 265 | static void _rt_start_loop() { | |
272 | #if LITMUS | 266 | #if LITMUS |
273 | #define START_LOOP \ | 267 | if (sleep_next_period() != 0) { |
274 | if (sleep_next_period() != 0) { \ | 268 | perror("Unable to sleep for next period"); |
275 | perror("Unable to sleep for next period"); \ | 269 | } |
276 | } \ | ||
277 | FLUSH_CACHES START_TIMER | ||
278 | #else | 270 | #else |
279 | #define START_LOOP sched_yield(); FLUSH_CACHES START_TIMER | 271 | sched_yield(); |
280 | #endif | 272 | #endif |
273 | FLUSH_CACHES | ||
274 | #if MMDC_PROF | ||
275 | /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ | ||
276 | start_mmdc_profiling(mmdc); | ||
277 | #endif | ||
278 | clock_gettime(CLOCK_MONOTONIC, &_rt_start); | ||
279 | } | ||
281 | 280 | ||
282 | #define STOP_LOOP STOP_TIMER jobs_complete++; SAVE_RESULTS | 281 | // Complete a job |
283 | 282 | static void _rt_stop_loop() { | |
283 | clock_gettime(CLOCK_MONOTONIC, &_rt_end); | ||
284 | #if MMDC_PROF | ||
285 | /* This freezes the profiling and makes results available */ | ||
286 | pause_mmdc_profiling(mmdc); | ||
287 | get_mmdc_profiling_results(mmdc, &mmdc_res); | ||
288 | #endif | ||
289 | _rt_save_job_result(); | ||
290 | _rt_jobs_complete++; | ||
291 | } | ||
284 | 292 | ||
285 | /* | 293 | /****** New API ****** |
286 | Intended structure | 294 | * Intended structure: |
295 | * | ||
296 | * |int main(int argc, char **argv) { | ||
297 | * | SET_UP | ||
298 | * | ... | ||
299 | * | for_each_job { | ||
300 | * | tacleInit(); | ||
301 | * | tacleMain(); | ||
302 | * | } | ||
303 | * | WRITE_TO_FILE | ||
304 | * |} | ||
305 | * | ||
306 | * The main() function must call its parameters argc and argv for SET_UP to be | ||
307 | * able to read them. | ||
308 | * Only SET_UP necessarily has to be in main(). | ||
309 | * | ||
310 | * We use some niche C features, here's a quick explaination: | ||
311 | * 1. The && operator doesn't evaluate the right-hand side of the expression | ||
312 | * unless the left side evaluated to true. We use this to only execute | ||
313 | * _rt_start_loop() when the loop will actually run. | ||
314 | * 2. The comma operator executes the first expression and then throws away the | ||
315 | * result. We use this to call our void function from inside a comparison. | ||
316 | */ | ||
317 | #define for_each_job \ | ||
318 | for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \ | ||
319 | _rt_stop_loop()) | ||
287 | 320 | ||
288 | main | 321 | /****** Legacy API ****** |
289 | SET_UP | 322 | * Intended structure: |
290 | notice that STOP LOOP negates the ++ if outout=0 | 323 | * |
291 | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){ | 324 | * |int main(int argc, char **argv) { |
292 | START_LOOP | 325 | * | SET_UP |
293 | tacleInit(); | 326 | * | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){ |
294 | tacleMain(); | 327 | * | START_LOOP |
295 | STOP_LOOP | 328 | * | tacleInit(); |
296 | } | 329 | * | tacleMain(); |
297 | WRITE_TO_FILE | 330 | * | STOP_LOOP |
298 | tacleReturn | 331 | * | } |
299 | */ | 332 | * | WRITE_TO_FILE |
333 | * | tacleReturn | ||
334 | * |} | ||
335 | * | ||
336 | * The main() function must call its parameters argc and argv for SET_UP to be | ||
337 | * able to read them. | ||
338 | */ | ||
339 | static int jobsComplete = 0; | ||
340 | #define START_LOOP _rt_start_loop(); | ||
341 | #define STOP_LOOP _rt_stop_loop(); | ||
342 | #define WRITE_TO_FILE _rt_write_to_file(); | ||
343 | #define maxJobs _rt_max_jobs | ||
344 | // Has been part of STOP_LOOP for quite some time | ||
345 | #define SAVE_RESULTS \ | ||
346 | #warning "The SAVE_RESULTS macro is deprecated and will soon be removed!"; | ||
347 | // Unclear if SLEEP is used anywhere. | ||
348 | #define SLEEP \ | ||
349 | #warning "The SLEEP macro is deprecated and may be removed!" \ | ||
350 | nanosleep((const struct timespec[]){{0, 1000000}}, NULL); | ||