]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /******************************************************************** |
2 | * COPYRIGHT: | |
4388f060 | 3 | * Copyright (c) 2002-2011, International Business Machines Corporation and |
b75a7d8f A |
4 | * others. All Rights Reserved. |
5 | ********************************************************************/ | |
6 | ||
4388f060 A |
7 | // Defines _XOPEN_SOURCE for access to POSIX functions. |
8 | // Must be before any other #includes. | |
9 | #include "uposixdefs.h" | |
46f4442e | 10 | |
73c04bcf A |
11 | #include "unicode/uperf.h" |
12 | #include "uoptions.h" | |
13 | #include "cmemory.h" | |
14 | #include <stdio.h> | |
15 | #include <stdlib.h> | |
b75a7d8f | 16 | |
73c04bcf | 17 | #if !UCONFIG_NO_CONVERSION |
4388f060 A |
18 | |
19 | UPerfFunction::~UPerfFunction() {} | |
20 | ||
b75a7d8f A |
21 | static const char delim = '/'; |
22 | static int32_t execCount = 0; | |
23 | UPerfTest* UPerfTest::gTest = NULL; | |
374ca955 A |
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" | |
46f4442e A |
36 | "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n" |
37 | "\t Cannot be used with --time\n" | |
374ca955 | 38 | "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n" |
46f4442e A |
39 | "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n" |
40 | "\t Cannot be used with --iterations\n" | |
374ca955 | 41 | "\t-l or --line-mode The data file should be processed in line mode\n" |
46f4442e A |
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" | |
374ca955 A |
44 | "\t-L or --locale Locale for the test\n"; |
45 | ||
b75a7d8f A |
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, | |
46f4442e A |
60 | LOCALE, |
61 | OPTIONS_COUNT | |
b75a7d8f A |
62 | }; |
63 | ||
64 | ||
46f4442e | 65 | static UOption options[OPTIONS_COUNT+20]={ |
374ca955 A |
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 | }; | |
b75a7d8f | 80 | |
46f4442e A |
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) { | |
b75a7d8f | 112 | //initialize the argument list |
46f4442e A |
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 | ||
b75a7d8f | 124 | //parse the arguments |
46f4442e A |
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 | } | |
b75a7d8f A |
131 | |
132 | // Now setup the arguments | |
46f4442e | 133 | if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) { |
b75a7d8f A |
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); | |
46f4442e A |
163 | if(options[TIME].doesOccur) { |
164 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
165 | return; | |
166 | } | |
167 | } else if(options[TIME].doesOccur) { | |
b75a7d8f | 168 | time = atoi(options[TIME].value); |
46f4442e A |
169 | } else { |
170 | iterations = 1000; // some default | |
b75a7d8f | 171 | } |
46f4442e | 172 | |
b75a7d8f A |
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) { | |
46f4442e | 184 | locale = options[LOCALE].value; |
b75a7d8f A |
185 | } |
186 | ||
187 | int32_t len = 0; | |
b75a7d8f A |
188 | if(fileName!=NULL){ |
189 | //pre-flight | |
73c04bcf | 190 | ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status); |
b75a7d8f | 191 | resolvedFileName = (char*) uprv_malloc(len); |
73c04bcf | 192 | if(resolvedFileName==NULL){ |
b75a7d8f A |
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){ | |
4388f060 A |
210 | if (U_FAILURE(status)) { |
211 | return NULL; | |
212 | } | |
213 | if (lines != NULL) { | |
214 | return lines; // don't do it again | |
215 | } | |
b75a7d8f A |
216 | lines = new ULine[MAXLINES]; |
217 | int maxLines = MAXLINES; | |
218 | numLines=0; | |
219 | const UChar* line=NULL; | |
220 | int32_t len =0; | |
221 | for (;;) { | |
374ca955 A |
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; | |
73c04bcf | 238 | delete []lines; |
374ca955 | 239 | return NULL; |
b75a7d8f | 240 | } |
374ca955 A |
241 | |
242 | memcpy(newLines, lines, numLines*sizeof(ULine)); | |
73c04bcf | 243 | delete []lines; |
374ca955 A |
244 | lines = newLines; |
245 | } | |
b75a7d8f A |
246 | } |
247 | return lines; | |
248 | } | |
249 | const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){ | |
46f4442e A |
250 | if (U_FAILURE(status)) { |
251 | return NULL; | |
252 | } | |
b75a7d8f A |
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 | name = ""; exec = exec; index = index; par = par; | |
337 | return NULL; | |
338 | } | |
339 | ||
340 | ||
341 | UBool UPerfTest::runTestLoop( char* testname, char* par ) | |
342 | { | |
343 | int32_t index = 0; | |
344 | const char* name; | |
345 | UBool run_this_test; | |
346 | UBool rval = FALSE; | |
347 | UErrorCode status = U_ZERO_ERROR; | |
348 | UPerfTest* saveTest = gTest; | |
349 | gTest = this; | |
350 | int32_t loops = 0; | |
351 | double t=0; | |
352 | int32_t n = 1; | |
46f4442e | 353 | long ops; |
b75a7d8f A |
354 | do { |
355 | this->runIndexedTest( index, FALSE, name ); | |
356 | if (!name || (name[0] == 0)) | |
357 | break; | |
358 | if (!testname) { | |
359 | run_this_test = TRUE; | |
360 | }else{ | |
361 | run_this_test = (UBool) (strcmp( name, testname ) == 0); | |
362 | } | |
363 | if (run_this_test) { | |
364 | UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par ); | |
365 | execCount++; | |
366 | rval=TRUE; | |
367 | if(testFunction==NULL){ | |
368 | fprintf(stderr,"%s function returned NULL", name); | |
369 | return FALSE; | |
370 | } | |
46f4442e A |
371 | ops = testFunction->getOperationsPerIteration(); |
372 | if (ops < 1) { | |
b75a7d8f A |
373 | fprintf(stderr, "%s returned an illegal operations/iteration()\n", name); |
374 | return FALSE; | |
375 | } | |
376 | if(iterations == 0) { | |
374ca955 A |
377 | n = time; |
378 | // Run for specified duration in seconds | |
379 | if(verbose==TRUE){ | |
380 | fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n); | |
381 | } | |
382 | ||
383 | //n *= 1000; // s => ms | |
384 | //System.out.println("# " + meth.getName() + " " + n + " sec"); | |
385 | int32_t failsafe = 1; // last resort for very fast methods | |
386 | t = 0; | |
387 | while (t < (int)(n * 0.9)) { // 90% is close enough | |
388 | if (loops == 0 || t == 0) { | |
389 | loops = failsafe; | |
390 | failsafe *= 10; | |
391 | } else { | |
392 | //System.out.println("# " + meth.getName() + " x " + loops + " = " + t); | |
393 | loops = (int)((double)n / t * loops + 0.5); | |
394 | if (loops == 0) { | |
395 | fprintf(stderr,"Unable to converge on desired duration"); | |
396 | return FALSE; | |
397 | } | |
398 | } | |
399 | //System.out.println("# " + meth.getName() + " x " + loops); | |
400 | t = testFunction->time(loops,&status); | |
401 | if(U_FAILURE(status)){ | |
402 | printf("Performance test failed with error: %s \n", u_errorName(status)); | |
403 | break; | |
404 | } | |
405 | } | |
b75a7d8f | 406 | } else { |
374ca955 | 407 | loops = iterations; |
b75a7d8f A |
408 | } |
409 | ||
46f4442e A |
410 | double min_t=1000000.0, sum_t=0.0; |
411 | long events = -1; | |
412 | ||
b75a7d8f | 413 | for(int32_t ps =0; ps < passes; ps++){ |
374ca955 A |
414 | fprintf(stdout,"= %s begin " ,name); |
415 | if(verbose==TRUE){ | |
416 | if(iterations > 0) { | |
417 | fprintf(stdout, "%i\n", (int)loops); | |
418 | } else { | |
419 | fprintf(stdout, "%i\n", (int)n); | |
420 | } | |
421 | } else { | |
422 | fprintf(stdout, "\n"); | |
423 | } | |
424 | t = testFunction->time(loops, &status); | |
425 | if(U_FAILURE(status)){ | |
426 | printf("Performance test failed with error: %s \n", u_errorName(status)); | |
427 | break; | |
428 | } | |
46f4442e A |
429 | sum_t+=t; |
430 | if(t<min_t) { | |
431 | min_t=t; | |
432 | } | |
374ca955 A |
433 | events = testFunction->getEventsPerIteration(); |
434 | //print info only in verbose mode | |
435 | if(verbose==TRUE){ | |
374ca955 | 436 | if(events == -1){ |
46f4442e | 437 | fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops); |
374ca955 | 438 | }else{ |
46f4442e | 439 | fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events); |
374ca955 A |
440 | } |
441 | }else{ | |
374ca955 | 442 | if(events == -1){ |
46f4442e | 443 | fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops); |
374ca955 | 444 | }else{ |
46f4442e | 445 | fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events); |
374ca955 A |
446 | } |
447 | } | |
b75a7d8f | 448 | } |
46f4442e A |
449 | if(verbose && U_SUCCESS(status)) { |
450 | double avg_t = sum_t/passes; | |
451 | if (loops == 0 || ops == 0) { | |
452 | fprintf(stderr, "%s did not run\n", name); | |
453 | } | |
454 | else if(events == -1) { | |
455 | fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n", | |
456 | name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops)); | |
457 | fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n", | |
458 | name, min_t, (int)loops, (min_t*1E9)/(loops*ops)); | |
459 | } | |
460 | else { | |
461 | fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n", | |
462 | name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events)); | |
463 | fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n", | |
464 | name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events)); | |
465 | } | |
466 | } | |
b75a7d8f A |
467 | delete testFunction; |
468 | } | |
469 | index++; | |
470 | }while(name); | |
471 | ||
472 | gTest = saveTest; | |
473 | return rval; | |
474 | } | |
475 | ||
476 | /** | |
477 | * Print a usage message for this test class. | |
478 | */ | |
479 | void UPerfTest::usage( void ) | |
480 | { | |
46f4442e A |
481 | puts(gUsageString); |
482 | if (_addUsage != NULL) { | |
483 | puts(_addUsage); | |
484 | } | |
485 | ||
b75a7d8f A |
486 | UBool save_verbose = verbose; |
487 | verbose = TRUE; | |
488 | fprintf(stdout,"Test names:\n"); | |
489 | fprintf(stdout,"-----------\n"); | |
490 | ||
491 | int32_t index = 0; | |
492 | const char* name = NULL; | |
493 | do{ | |
494 | this->runIndexedTest( index, FALSE, name ); | |
374ca955 A |
495 | if (!name) |
496 | break; | |
4388f060 | 497 | fprintf(stdout, "%s\n", name); |
b75a7d8f A |
498 | index++; |
499 | }while (name && (name[0] != 0)); | |
500 | verbose = save_verbose; | |
501 | } | |
502 | ||
503 | ||
504 | ||
505 | ||
506 | void UPerfTest::setCaller( UPerfTest* callingTest ) | |
507 | { | |
508 | caller = callingTest; | |
509 | if (caller) { | |
510 | verbose = caller->verbose; | |
511 | } | |
512 | } | |
513 | ||
514 | UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par ) | |
515 | { | |
516 | execCount--; // correct a previously assumed test-exec, as this only calls a subtest | |
517 | testToBeCalled.setCaller( this ); | |
518 | return testToBeCalled.runTest( path, par ); | |
519 | } | |
520 | ||
521 | UPerfTest::~UPerfTest(){ | |
522 | if(lines!=NULL){ | |
523 | delete[] lines; | |
524 | } | |
525 | if(buffer!=NULL){ | |
526 | uprv_free(buffer); | |
527 | } | |
73c04bcf A |
528 | if(resolvedFileName!=NULL){ |
529 | uprv_free(resolvedFileName); | |
530 | } | |
b75a7d8f A |
531 | ucbuf_close(ucharBuf); |
532 | } | |
533 | ||
73c04bcf | 534 | #endif |