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