1 /********************************************************************
3 * Copyright (c) 2002-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
7 // Defines _XOPEN_SOURCE for access to POSIX functions.
8 // Must be before any other #includes.
9 #include "uposixdefs.h"
11 #include "unicode/uperf.h"
17 #if !UCONFIG_NO_CONVERSION
19 UPerfFunction::~UPerfFunction() {}
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"
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";
65 static UOption options
[OPTIONS_COUNT
+20]={
67 UOPTION_HELP_QUESTION_MARK
,
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
)
81 UPerfTest::UPerfTest(int32_t argc
, const char* argv
[], UErrorCode
& status
)
82 : _argc(argc
), _argv(argv
), _addUsage(NULL
),
83 ucharBuf(NULL
), encoding(""),
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),
91 init(NULL
, 0, status
);
94 UPerfTest::UPerfTest(int32_t argc
, const char* argv
[],
95 UOption addOptions
[], int32_t addOptionsCount
,
98 : _argc(argc
), _argv(argv
), _addUsage(addUsage
),
99 ucharBuf(NULL
), encoding(""),
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),
107 init(addOptions
, addOptionsCount
, status
);
110 void UPerfTest::init(UOption addOptions
[], int32_t addOptionsCount
,
111 UErrorCode
& status
) {
112 //initialize the argument list
113 U_MAIN_INIT_ARGS(_argc
, _argv
);
115 resolvedFileName
= NULL
;
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
;
124 //parse the arguments
125 _remainingArgc
= u_parseArgs(_argc
, (char**)_argv
, optionsCount
, options
);
127 // copy back values for additional options
128 if (addOptionsCount
> 0) {
129 memcpy(addOptions
, options
+OPTIONS_COUNT
, addOptionsCount
*sizeof(UOption
));
132 // Now setup the arguments
133 if(_argc
==1 || options
[HELP1
].doesOccur
|| options
[HELP2
].doesOccur
) {
134 status
= U_ILLEGAL_ARGUMENT_ERROR
;
138 if(options
[VERBOSE
].doesOccur
) {
142 if(options
[SOURCEDIR
].doesOccur
) {
143 sourceDir
= options
[SOURCEDIR
].value
;
146 if(options
[ENCODING
].doesOccur
) {
147 encoding
= options
[ENCODING
].value
;
150 if(options
[USELEN
].doesOccur
) {
154 if(options
[FILE_NAME
].doesOccur
){
155 fileName
= options
[FILE_NAME
].value
;
158 if(options
[PASSES
].doesOccur
) {
159 passes
= atoi(options
[PASSES
].value
);
161 if(options
[ITERATIONS
].doesOccur
) {
162 iterations
= atoi(options
[ITERATIONS
].value
);
163 if(options
[TIME
].doesOccur
) {
164 status
= U_ILLEGAL_ARGUMENT_ERROR
;
167 } else if(options
[TIME
].doesOccur
) {
168 time
= atoi(options
[TIME
].value
);
170 iterations
= 1000; // some default
173 if(options
[LINE_MODE
].doesOccur
) {
178 if(options
[BULK_MODE
].doesOccur
) {
183 if(options
[LOCALE
].doesOccur
) {
184 locale
= options
[LOCALE
].value
;
190 ucbuf_resolveFileName(sourceDir
, fileName
, NULL
, &len
, &status
);
191 resolvedFileName
= (char*) uprv_malloc(len
);
192 if(resolvedFileName
==NULL
){
193 status
= U_MEMORY_ALLOCATION_ERROR
;
196 if(status
== U_BUFFER_OVERFLOW_ERROR
){
197 status
= U_ZERO_ERROR
;
199 ucbuf_resolveFileName(sourceDir
, fileName
, resolvedFileName
, &len
, &status
);
200 ucharBuf
= ucbuf_open(resolvedFileName
,&encoding
,TRUE
,FALSE
,&status
);
202 if(U_FAILURE(status
)){
203 printf("Could not open the input file %s. Error: %s\n", fileName
, u_errorName(status
));
209 ULine
* UPerfTest::getLines(UErrorCode
& status
){
210 if (U_FAILURE(status
)) {
214 return lines
; // don't do it again
216 lines
= new ULine
[MAXLINES
];
217 int maxLines
= MAXLINES
;
219 const UChar
* line
=NULL
;
222 line
= ucbuf_readline(ucharBuf
,&len
,&status
);
223 if(line
== NULL
|| U_FAILURE(status
)){
226 lines
[numLines
].name
= new UChar
[len
];
227 lines
[numLines
].len
= len
;
228 memcpy(lines
[numLines
].name
, line
, len
* U_SIZEOF_UCHAR
);
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
;
242 memcpy(newLines
, lines
, numLines
*sizeof(ULine
));
249 const UChar
* UPerfTest::getBuffer(int32_t& len
, UErrorCode
& status
){
250 if (U_FAILURE(status
)) {
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
);
260 UBool
UPerfTest::run(){
261 if(_remainingArgc
==1){
262 // Testing all methods
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
];
271 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
272 //fprintf(stdout, "\n%s:\n", name);
274 char* parameter
= strchr( name
, '@' );
280 res
= runTest( name
, parameter
);
281 if (!res
|| (execCount
<= 0)) {
282 fprintf(stdout
, "\n---ERROR: Test doesn't exist: %s!\n", name
);
289 UBool
UPerfTest::runTest(char* name
, char* par
){
294 pos
= strchr( name
, delim
); // check if name contains path (by looking for '/')
296 path
= pos
+1; // store subpath for calling subtest
297 *pos
= 0; // split into two strings
302 if (!name
|| (name
[0] == 0) || (strcmp(name
, "*") == 0)) {
303 rval
= runTestLoop( NULL
, NULL
);
305 }else if (strcmp( name
, "LIST" ) == 0) {
310 rval
= runTestLoop( name
, par
);
314 *pos
= delim
; // restore original value at pos
319 void UPerfTest::setPath( char* pathVal
)
321 this->path
= pathVal
;
324 // call individual tests, to be overriden to call implementations
325 UPerfFunction
* UPerfTest::runIndexedTest( int32_t /*index*/, UBool
/*exec*/, const char* & /*name*/, char* /*par*/ )
327 // to be overriden by a method like:
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;
335 fprintf(stderr
,"*** runIndexedTest needs to be overriden! ***");
340 UBool
UPerfTest::runTestLoop( char* testname
, char* par
)
346 UErrorCode status
= U_ZERO_ERROR
;
347 UPerfTest
* saveTest
= gTest
;
354 this->runIndexedTest( index
, FALSE
, name
);
355 if (!name
|| (name
[0] == 0))
358 run_this_test
= TRUE
;
360 run_this_test
= (UBool
) (strcmp( name
, testname
) == 0);
363 UPerfFunction
* testFunction
= this->runIndexedTest( index
, TRUE
, name
, par
);
366 if(testFunction
==NULL
){
367 fprintf(stderr
,"%s function returned NULL", name
);
370 ops
= testFunction
->getOperationsPerIteration();
372 fprintf(stderr
, "%s returned an illegal operations/iteration()\n", name
);
375 if(iterations
== 0) {
377 // Run for specified duration in seconds
379 fprintf(stdout
,"= %s calibrating %i seconds \n", name
, (int)n
);
382 //n *= 1000; // s => ms
383 //System.out.println("# " + meth.getName() + " " + n + " sec");
384 int32_t failsafe
= 1; // last resort for very fast methods
386 while (t
< (int)(n
* 0.9)) { // 90% is close enough
387 if (loops
== 0 || t
== 0) {
391 //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
392 loops
= (int)((double)n
/ t
* loops
+ 0.5);
394 fprintf(stderr
,"Unable to converge on desired duration");
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
));
409 double min_t
=1000000.0, sum_t
=0.0;
412 for(int32_t ps
=0; ps
< passes
; ps
++){
413 fprintf(stdout
,"= %s begin " ,name
);
416 fprintf(stdout
, "%i\n", (int)loops
);
418 fprintf(stdout
, "%i\n", (int)n
);
421 fprintf(stdout
, "\n");
423 t
= testFunction
->time(loops
, &status
);
424 if(U_FAILURE(status
)){
425 printf("Performance test failed with error: %s \n", u_errorName(status
));
432 events
= testFunction
->getEventsPerIteration();
433 //print info only in verbose mode
436 fprintf(stdout
, "= %s end: %f loops: %i operations: %li \n", name
, t
, (int)loops
, ops
);
438 fprintf(stdout
, "= %s end: %f loops: %i operations: %li events: %li\n", name
, t
, (int)loops
, ops
, events
);
442 fprintf(stdout
,"= %s end %f %i %li\n", name
, t
, (int)loops
, ops
);
444 fprintf(stdout
,"= %s end %f %i %li %li\n", name
, t
, (int)loops
, ops
, events
);
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
);
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
));
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
));
476 * Print a usage message for this test class.
478 void UPerfTest::usage( void )
481 if (_addUsage
!= NULL
) {
485 UBool save_verbose
= verbose
;
487 fprintf(stdout
,"Test names:\n");
488 fprintf(stdout
,"-----------\n");
491 const char* name
= NULL
;
493 this->runIndexedTest( index
, FALSE
, name
);
496 fprintf(stdout
, "%s\n", name
);
498 }while (name
&& (name
[0] != 0));
499 verbose
= save_verbose
;
505 void UPerfTest::setCaller( UPerfTest
* callingTest
)
507 caller
= callingTest
;
509 verbose
= caller
->verbose
;
513 UBool
UPerfTest::callTest( UPerfTest
& testToBeCalled
, char* par
)
515 execCount
--; // correct a previously assumed test-exec, as this only calls a subtest
516 testToBeCalled
.setCaller( this );
517 return testToBeCalled
.runTest( path
, par
);
520 UPerfTest::~UPerfTest(){
527 if(resolvedFileName
!=NULL
){
528 uprv_free(resolvedFileName
);
530 ucbuf_close(ucharBuf
);