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