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