|
1 /******************************************************************** |
|
2 * COPYRIGHT: |
|
3 * Copyright (c) 2002-2012, International Business Machines Corporation and |
|
4 * others. All Rights Reserved. |
|
5 ********************************************************************/ |
|
6 |
|
7 // Defines _XOPEN_SOURCE for access to POSIX functions. |
|
8 // Must be before any other #includes. |
|
9 #include "uposixdefs.h" |
|
10 |
|
11 #include "unicode/uperf.h" |
|
12 #include "uoptions.h" |
|
13 #include "cmemory.h" |
|
14 #include <stdio.h> |
|
15 #include <stdlib.h> |
|
16 |
|
17 #if !UCONFIG_NO_CONVERSION |
|
18 |
|
19 UPerfFunction::~UPerfFunction() {} |
|
20 |
|
21 static const char delim = '/'; |
|
22 static int32_t execCount = 0; |
|
23 UPerfTest* UPerfTest::gTest = NULL; |
|
24 static const int MAXLINES = 40000; |
|
25 const char UPerfTest::gUsageString[] = |
|
26 "Usage: %s [OPTIONS] [FILES]\n" |
|
27 "\tReads the input file and prints out time taken in seconds\n" |
|
28 "Options:\n" |
|
29 "\t-h or -? or --help this usage text\n" |
|
30 "\t-v or --verbose print extra information when processing files\n" |
|
31 "\t-s or --sourcedir source directory for files followed by path\n" |
|
32 "\t followed by path\n" |
|
33 "\t-e or --encoding encoding of source files\n" |
|
34 "\t-u or --uselen perform timing analysis on non-null terminated buffer using length\n" |
|
35 "\t-f or --file-name file to be used as input data\n" |
|
36 "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n" |
|
37 "\t Cannot be used with --time\n" |
|
38 "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n" |
|
39 "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n" |
|
40 "\t Cannot be used with --iterations\n" |
|
41 "\t-l or --line-mode The data file should be processed in line mode\n" |
|
42 "\t-b or --bulk-mode The data file should be processed in file based.\n" |
|
43 "\t Cannot be used with --line-mode\n" |
|
44 "\t-L or --locale Locale for the test\n"; |
|
45 |
|
46 enum |
|
47 { |
|
48 HELP1, |
|
49 HELP2, |
|
50 VERBOSE, |
|
51 SOURCEDIR, |
|
52 ENCODING, |
|
53 USELEN, |
|
54 FILE_NAME, |
|
55 PASSES, |
|
56 ITERATIONS, |
|
57 TIME, |
|
58 LINE_MODE, |
|
59 BULK_MODE, |
|
60 LOCALE, |
|
61 OPTIONS_COUNT |
|
62 }; |
|
63 |
|
64 |
|
65 static UOption options[OPTIONS_COUNT+20]={ |
|
66 UOPTION_HELP_H, |
|
67 UOPTION_HELP_QUESTION_MARK, |
|
68 UOPTION_VERBOSE, |
|
69 UOPTION_SOURCEDIR, |
|
70 UOPTION_ENCODING, |
|
71 UOPTION_DEF( "uselen", 'u', UOPT_NO_ARG), |
|
72 UOPTION_DEF( "file-name", 'f', UOPT_REQUIRES_ARG), |
|
73 UOPTION_DEF( "passes", 'p', UOPT_REQUIRES_ARG), |
|
74 UOPTION_DEF( "iterations", 'i', UOPT_REQUIRES_ARG), |
|
75 UOPTION_DEF( "time", 't', UOPT_REQUIRES_ARG), |
|
76 UOPTION_DEF( "line-mode", 'l', UOPT_NO_ARG), |
|
77 UOPTION_DEF( "bulk-mode", 'b', UOPT_NO_ARG), |
|
78 UOPTION_DEF( "locale", 'L', UOPT_REQUIRES_ARG) |
|
79 }; |
|
80 |
|
81 UPerfTest::UPerfTest(int32_t argc, const char* argv[], UErrorCode& status) |
|
82 : _argc(argc), _argv(argv), _addUsage(NULL), |
|
83 ucharBuf(NULL), encoding(""), |
|
84 uselen(FALSE), |
|
85 fileName(NULL), sourceDir("."), |
|
86 lines(NULL), numLines(0), line_mode(TRUE), |
|
87 buffer(NULL), bufferLen(0), |
|
88 verbose(FALSE), bulk_mode(FALSE), |
|
89 passes(1), iterations(0), time(0), |
|
90 locale(NULL) { |
|
91 init(NULL, 0, status); |
|
92 } |
|
93 |
|
94 UPerfTest::UPerfTest(int32_t argc, const char* argv[], |
|
95 UOption addOptions[], int32_t addOptionsCount, |
|
96 const char *addUsage, |
|
97 UErrorCode& status) |
|
98 : _argc(argc), _argv(argv), _addUsage(addUsage), |
|
99 ucharBuf(NULL), encoding(""), |
|
100 uselen(FALSE), |
|
101 fileName(NULL), sourceDir("."), |
|
102 lines(NULL), numLines(0), line_mode(TRUE), |
|
103 buffer(NULL), bufferLen(0), |
|
104 verbose(FALSE), bulk_mode(FALSE), |
|
105 passes(1), iterations(0), time(0), |
|
106 locale(NULL) { |
|
107 init(addOptions, addOptionsCount, status); |
|
108 } |
|
109 |
|
110 void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount, |
|
111 UErrorCode& status) { |
|
112 //initialize the argument list |
|
113 U_MAIN_INIT_ARGS(_argc, _argv); |
|
114 |
|
115 resolvedFileName = NULL; |
|
116 |
|
117 // add specific options |
|
118 int32_t optionsCount = OPTIONS_COUNT; |
|
119 if (addOptionsCount > 0) { |
|
120 memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption)); |
|
121 optionsCount += addOptionsCount; |
|
122 } |
|
123 |
|
124 //parse the arguments |
|
125 _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options); |
|
126 |
|
127 // copy back values for additional options |
|
128 if (addOptionsCount > 0) { |
|
129 memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption)); |
|
130 } |
|
131 |
|
132 // Now setup the arguments |
|
133 if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) { |
|
134 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
135 return; |
|
136 } |
|
137 |
|
138 if(options[VERBOSE].doesOccur) { |
|
139 verbose = TRUE; |
|
140 } |
|
141 |
|
142 if(options[SOURCEDIR].doesOccur) { |
|
143 sourceDir = options[SOURCEDIR].value; |
|
144 } |
|
145 |
|
146 if(options[ENCODING].doesOccur) { |
|
147 encoding = options[ENCODING].value; |
|
148 } |
|
149 |
|
150 if(options[USELEN].doesOccur) { |
|
151 uselen = TRUE; |
|
152 } |
|
153 |
|
154 if(options[FILE_NAME].doesOccur){ |
|
155 fileName = options[FILE_NAME].value; |
|
156 } |
|
157 |
|
158 if(options[PASSES].doesOccur) { |
|
159 passes = atoi(options[PASSES].value); |
|
160 } |
|
161 if(options[ITERATIONS].doesOccur) { |
|
162 iterations = atoi(options[ITERATIONS].value); |
|
163 if(options[TIME].doesOccur) { |
|
164 status = U_ILLEGAL_ARGUMENT_ERROR; |
|
165 return; |
|
166 } |
|
167 } else if(options[TIME].doesOccur) { |
|
168 time = atoi(options[TIME].value); |
|
169 } else { |
|
170 iterations = 1000; // some default |
|
171 } |
|
172 |
|
173 if(options[LINE_MODE].doesOccur) { |
|
174 line_mode = TRUE; |
|
175 bulk_mode = FALSE; |
|
176 } |
|
177 |
|
178 if(options[BULK_MODE].doesOccur) { |
|
179 bulk_mode = TRUE; |
|
180 line_mode = FALSE; |
|
181 } |
|
182 |
|
183 if(options[LOCALE].doesOccur) { |
|
184 locale = options[LOCALE].value; |
|
185 } |
|
186 |
|
187 int32_t len = 0; |
|
188 if(fileName!=NULL){ |
|
189 //pre-flight |
|
190 ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status); |
|
191 resolvedFileName = (char*) uprv_malloc(len); |
|
192 if(resolvedFileName==NULL){ |
|
193 status= U_MEMORY_ALLOCATION_ERROR; |
|
194 return; |
|
195 } |
|
196 if(status == U_BUFFER_OVERFLOW_ERROR){ |
|
197 status = U_ZERO_ERROR; |
|
198 } |
|
199 ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status); |
|
200 ucharBuf = ucbuf_open(resolvedFileName,&encoding,TRUE,FALSE,&status); |
|
201 |
|
202 if(U_FAILURE(status)){ |
|
203 printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status)); |
|
204 return; |
|
205 } |
|
206 } |
|
207 } |
|
208 |
|
209 ULine* UPerfTest::getLines(UErrorCode& status){ |
|
210 if (U_FAILURE(status)) { |
|
211 return NULL; |
|
212 } |
|
213 if (lines != NULL) { |
|
214 return lines; // don't do it again |
|
215 } |
|
216 lines = new ULine[MAXLINES]; |
|
217 int maxLines = MAXLINES; |
|
218 numLines=0; |
|
219 const UChar* line=NULL; |
|
220 int32_t len =0; |
|
221 for (;;) { |
|
222 line = ucbuf_readline(ucharBuf,&len,&status); |
|
223 if(line == NULL || U_FAILURE(status)){ |
|
224 break; |
|
225 } |
|
226 lines[numLines].name = new UChar[len]; |
|
227 lines[numLines].len = len; |
|
228 memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR); |
|
229 |
|
230 numLines++; |
|
231 len = 0; |
|
232 if (numLines >= maxLines) { |
|
233 maxLines += MAXLINES; |
|
234 ULine *newLines = new ULine[maxLines]; |
|
235 if(newLines == NULL) { |
|
236 fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines); |
|
237 status= U_MEMORY_ALLOCATION_ERROR; |
|
238 delete []lines; |
|
239 return NULL; |
|
240 } |
|
241 |
|
242 memcpy(newLines, lines, numLines*sizeof(ULine)); |
|
243 delete []lines; |
|
244 lines = newLines; |
|
245 } |
|
246 } |
|
247 return lines; |
|
248 } |
|
249 const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){ |
|
250 if (U_FAILURE(status)) { |
|
251 return NULL; |
|
252 } |
|
253 len = ucbuf_size(ucharBuf); |
|
254 buffer = (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1)); |
|
255 u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len); |
|
256 buffer[len]=0; |
|
257 len = bufferLen; |
|
258 return buffer; |
|
259 } |
|
260 UBool UPerfTest::run(){ |
|
261 if(_remainingArgc==1){ |
|
262 // Testing all methods |
|
263 return runTest(); |
|
264 } |
|
265 UBool res=FALSE; |
|
266 // Test only the specified fucntion |
|
267 for (int i = 1; i < _remainingArgc; ++i) { |
|
268 if (_argv[i][0] != '-') { |
|
269 char* name = (char*) _argv[i]; |
|
270 if(verbose==TRUE){ |
|
271 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name); |
|
272 //fprintf(stdout, "\n%s:\n", name); |
|
273 } |
|
274 char* parameter = strchr( name, '@' ); |
|
275 if (parameter) { |
|
276 *parameter = 0; |
|
277 parameter += 1; |
|
278 } |
|
279 execCount = 0; |
|
280 res = runTest( name, parameter ); |
|
281 if (!res || (execCount <= 0)) { |
|
282 fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name); |
|
283 return FALSE; |
|
284 } |
|
285 } |
|
286 } |
|
287 return res; |
|
288 } |
|
289 UBool UPerfTest::runTest(char* name, char* par ){ |
|
290 UBool rval; |
|
291 char* pos = NULL; |
|
292 |
|
293 if (name) |
|
294 pos = strchr( name, delim ); // check if name contains path (by looking for '/') |
|
295 if (pos) { |
|
296 path = pos+1; // store subpath for calling subtest |
|
297 *pos = 0; // split into two strings |
|
298 }else{ |
|
299 path = NULL; |
|
300 } |
|
301 |
|
302 if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) { |
|
303 rval = runTestLoop( NULL, NULL ); |
|
304 |
|
305 }else if (strcmp( name, "LIST" ) == 0) { |
|
306 this->usage(); |
|
307 rval = TRUE; |
|
308 |
|
309 }else{ |
|
310 rval = runTestLoop( name, par ); |
|
311 } |
|
312 |
|
313 if (pos) |
|
314 *pos = delim; // restore original value at pos |
|
315 return rval; |
|
316 } |
|
317 |
|
318 |
|
319 void UPerfTest::setPath( char* pathVal ) |
|
320 { |
|
321 this->path = pathVal; |
|
322 } |
|
323 |
|
324 // call individual tests, to be overriden to call implementations |
|
325 UPerfFunction* UPerfTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ ) |
|
326 { |
|
327 // to be overriden by a method like: |
|
328 /* |
|
329 switch (index) { |
|
330 case 0: name = "First Test"; if (exec) FirstTest( par ); break; |
|
331 case 1: name = "Second Test"; if (exec) SecondTest( par ); break; |
|
332 default: name = ""; break; |
|
333 } |
|
334 */ |
|
335 fprintf(stderr,"*** runIndexedTest needs to be overriden! ***"); |
|
336 return NULL; |
|
337 } |
|
338 |
|
339 |
|
340 UBool UPerfTest::runTestLoop( char* testname, char* par ) |
|
341 { |
|
342 int32_t index = 0; |
|
343 const char* name; |
|
344 UBool run_this_test; |
|
345 UBool rval = FALSE; |
|
346 UErrorCode status = U_ZERO_ERROR; |
|
347 UPerfTest* saveTest = gTest; |
|
348 gTest = this; |
|
349 int32_t loops = 0; |
|
350 double t=0; |
|
351 int32_t n = 1; |
|
352 long ops; |
|
353 do { |
|
354 this->runIndexedTest( index, FALSE, name ); |
|
355 if (!name || (name[0] == 0)) |
|
356 break; |
|
357 if (!testname) { |
|
358 run_this_test = TRUE; |
|
359 }else{ |
|
360 run_this_test = (UBool) (strcmp( name, testname ) == 0); |
|
361 } |
|
362 if (run_this_test) { |
|
363 UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par ); |
|
364 execCount++; |
|
365 rval=TRUE; |
|
366 if(testFunction==NULL){ |
|
367 fprintf(stderr,"%s function returned NULL", name); |
|
368 return FALSE; |
|
369 } |
|
370 ops = testFunction->getOperationsPerIteration(); |
|
371 if (ops < 1) { |
|
372 fprintf(stderr, "%s returned an illegal operations/iteration()\n", name); |
|
373 return FALSE; |
|
374 } |
|
375 if(iterations == 0) { |
|
376 n = time; |
|
377 // Run for specified duration in seconds |
|
378 if(verbose==TRUE){ |
|
379 fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n); |
|
380 } |
|
381 |
|
382 //n *= 1000; // s => ms |
|
383 //System.out.println("# " + meth.getName() + " " + n + " sec"); |
|
384 int32_t failsafe = 1; // last resort for very fast methods |
|
385 t = 0; |
|
386 while (t < (int)(n * 0.9)) { // 90% is close enough |
|
387 if (loops == 0 || t == 0) { |
|
388 loops = failsafe; |
|
389 failsafe *= 10; |
|
390 } else { |
|
391 //System.out.println("# " + meth.getName() + " x " + loops + " = " + t); |
|
392 loops = (int)((double)n / t * loops + 0.5); |
|
393 if (loops == 0) { |
|
394 fprintf(stderr,"Unable to converge on desired duration"); |
|
395 return FALSE; |
|
396 } |
|
397 } |
|
398 //System.out.println("# " + meth.getName() + " x " + loops); |
|
399 t = testFunction->time(loops,&status); |
|
400 if(U_FAILURE(status)){ |
|
401 printf("Performance test failed with error: %s \n", u_errorName(status)); |
|
402 break; |
|
403 } |
|
404 } |
|
405 } else { |
|
406 loops = iterations; |
|
407 } |
|
408 |
|
409 double min_t=1000000.0, sum_t=0.0; |
|
410 long events = -1; |
|
411 |
|
412 for(int32_t ps =0; ps < passes; ps++){ |
|
413 fprintf(stdout,"= %s begin " ,name); |
|
414 if(verbose==TRUE){ |
|
415 if(iterations > 0) { |
|
416 fprintf(stdout, "%i\n", (int)loops); |
|
417 } else { |
|
418 fprintf(stdout, "%i\n", (int)n); |
|
419 } |
|
420 } else { |
|
421 fprintf(stdout, "\n"); |
|
422 } |
|
423 t = testFunction->time(loops, &status); |
|
424 if(U_FAILURE(status)){ |
|
425 printf("Performance test failed with error: %s \n", u_errorName(status)); |
|
426 break; |
|
427 } |
|
428 sum_t+=t; |
|
429 if(t<min_t) { |
|
430 min_t=t; |
|
431 } |
|
432 events = testFunction->getEventsPerIteration(); |
|
433 //print info only in verbose mode |
|
434 if(verbose==TRUE){ |
|
435 if(events == -1){ |
|
436 fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops); |
|
437 }else{ |
|
438 fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events); |
|
439 } |
|
440 }else{ |
|
441 if(events == -1){ |
|
442 fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops); |
|
443 }else{ |
|
444 fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events); |
|
445 } |
|
446 } |
|
447 } |
|
448 if(verbose && U_SUCCESS(status)) { |
|
449 double avg_t = sum_t/passes; |
|
450 if (loops == 0 || ops == 0) { |
|
451 fprintf(stderr, "%s did not run\n", name); |
|
452 } |
|
453 else if(events == -1) { |
|
454 fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n", |
|
455 name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops)); |
|
456 fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n", |
|
457 name, min_t, (int)loops, (min_t*1E9)/(loops*ops)); |
|
458 } |
|
459 else { |
|
460 fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n", |
|
461 name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events)); |
|
462 fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n", |
|
463 name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events)); |
|
464 } |
|
465 } |
|
466 delete testFunction; |
|
467 } |
|
468 index++; |
|
469 }while(name); |
|
470 |
|
471 gTest = saveTest; |
|
472 return rval; |
|
473 } |
|
474 |
|
475 /** |
|
476 * Print a usage message for this test class. |
|
477 */ |
|
478 void UPerfTest::usage( void ) |
|
479 { |
|
480 puts(gUsageString); |
|
481 if (_addUsage != NULL) { |
|
482 puts(_addUsage); |
|
483 } |
|
484 |
|
485 UBool save_verbose = verbose; |
|
486 verbose = TRUE; |
|
487 fprintf(stdout,"Test names:\n"); |
|
488 fprintf(stdout,"-----------\n"); |
|
489 |
|
490 int32_t index = 0; |
|
491 const char* name = NULL; |
|
492 do{ |
|
493 this->runIndexedTest( index, FALSE, name ); |
|
494 if (!name) |
|
495 break; |
|
496 fprintf(stdout, "%s\n", name); |
|
497 index++; |
|
498 }while (name && (name[0] != 0)); |
|
499 verbose = save_verbose; |
|
500 } |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 void UPerfTest::setCaller( UPerfTest* callingTest ) |
|
506 { |
|
507 caller = callingTest; |
|
508 if (caller) { |
|
509 verbose = caller->verbose; |
|
510 } |
|
511 } |
|
512 |
|
513 UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par ) |
|
514 { |
|
515 execCount--; // correct a previously assumed test-exec, as this only calls a subtest |
|
516 testToBeCalled.setCaller( this ); |
|
517 return testToBeCalled.runTest( path, par ); |
|
518 } |
|
519 |
|
520 UPerfTest::~UPerfTest(){ |
|
521 if(lines!=NULL){ |
|
522 delete[] lines; |
|
523 } |
|
524 if(buffer!=NULL){ |
|
525 uprv_free(buffer); |
|
526 } |
|
527 if(resolvedFileName!=NULL){ |
|
528 uprv_free(resolvedFileName); |
|
529 } |
|
530 ucbuf_close(ucharBuf); |
|
531 } |
|
532 |
|
533 #endif |