1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
5 * Copyright (c) 2002-2012, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
9 // Defines _XOPEN_SOURCE for access to POSIX functions.
10 // Must be before any other #includes.
11 #include "uposixdefs.h"
13 #include "unicode/uperf.h"
19 #if !UCONFIG_NO_CONVERSION
21 UPerfFunction::~UPerfFunction() {}
23 static const char delim
= '/';
24 static int32_t execCount
= 0;
25 UPerfTest
* UPerfTest::gTest
= NULL
;
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"
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"
38 "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n"
39 "\t Cannot be used with --time\n"
40 "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n"
41 "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n"
42 "\t Cannot be used with --iterations\n"
43 "\t-l or --line-mode The data file should be processed in line mode\n"
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"
46 "\t-L or --locale Locale for the test\n";
67 static UOption options
[OPTIONS_COUNT
+20]={
69 UOPTION_HELP_QUESTION_MARK
,
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
)
83 UPerfTest::UPerfTest(int32_t argc
, const char* argv
[], UErrorCode
& status
)
84 : _argc(argc
), _argv(argv
), _addUsage(NULL
),
85 ucharBuf(NULL
), encoding(""),
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),
93 init(NULL
, 0, status
);
96 UPerfTest::UPerfTest(int32_t argc
, const char* argv
[],
97 UOption addOptions
[], int32_t addOptionsCount
,
100 : _argc(argc
), _argv(argv
), _addUsage(addUsage
),
101 ucharBuf(NULL
), encoding(""),
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),
109 init(addOptions
, addOptionsCount
, status
);
112 void UPerfTest::init(UOption addOptions
[], int32_t addOptionsCount
,
113 UErrorCode
& status
) {
114 //initialize the argument list
115 U_MAIN_INIT_ARGS(_argc
, _argv
);
117 resolvedFileName
= NULL
;
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
;
126 //parse the arguments
127 _remainingArgc
= u_parseArgs(_argc
, (char**)_argv
, optionsCount
, options
);
129 // copy back values for additional options
130 if (addOptionsCount
> 0) {
131 memcpy(addOptions
, options
+OPTIONS_COUNT
, addOptionsCount
*sizeof(UOption
));
134 // Now setup the arguments
135 if(_argc
==1 || options
[HELP1
].doesOccur
|| options
[HELP2
].doesOccur
) {
136 status
= U_ILLEGAL_ARGUMENT_ERROR
;
140 if(options
[VERBOSE
].doesOccur
) {
144 if(options
[SOURCEDIR
].doesOccur
) {
145 sourceDir
= options
[SOURCEDIR
].value
;
148 if(options
[ENCODING
].doesOccur
) {
149 encoding
= options
[ENCODING
].value
;
152 if(options
[USELEN
].doesOccur
) {
156 if(options
[FILE_NAME
].doesOccur
){
157 fileName
= options
[FILE_NAME
].value
;
160 if(options
[PASSES
].doesOccur
) {
161 passes
= atoi(options
[PASSES
].value
);
163 if(options
[ITERATIONS
].doesOccur
) {
164 iterations
= atoi(options
[ITERATIONS
].value
);
165 if(options
[TIME
].doesOccur
) {
166 status
= U_ILLEGAL_ARGUMENT_ERROR
;
169 } else if(options
[TIME
].doesOccur
) {
170 time
= atoi(options
[TIME
].value
);
172 iterations
= 1000; // some default
175 if(options
[LINE_MODE
].doesOccur
) {
180 if(options
[BULK_MODE
].doesOccur
) {
185 if(options
[LOCALE
].doesOccur
) {
186 locale
= options
[LOCALE
].value
;
192 ucbuf_resolveFileName(sourceDir
, fileName
, NULL
, &len
, &status
);
193 resolvedFileName
= (char*) uprv_malloc(len
);
194 if(resolvedFileName
==NULL
){
195 status
= U_MEMORY_ALLOCATION_ERROR
;
198 if(status
== U_BUFFER_OVERFLOW_ERROR
){
199 status
= U_ZERO_ERROR
;
201 ucbuf_resolveFileName(sourceDir
, fileName
, resolvedFileName
, &len
, &status
);
202 ucharBuf
= ucbuf_open(resolvedFileName
,&encoding
,TRUE
,FALSE
,&status
);
204 if(U_FAILURE(status
)){
205 printf("Could not open the input file %s. Error: %s\n", fileName
, u_errorName(status
));
211 ULine
* UPerfTest::getLines(UErrorCode
& status
){
212 if (U_FAILURE(status
)) {
216 return lines
; // don't do it again
218 lines
= new ULine
[MAXLINES
];
219 int maxLines
= MAXLINES
;
221 const UChar
* line
=NULL
;
224 line
= ucbuf_readline(ucharBuf
,&len
,&status
);
225 if(line
== NULL
|| U_FAILURE(status
)){
228 lines
[numLines
].name
= new UChar
[len
];
229 lines
[numLines
].len
= len
;
230 memcpy(lines
[numLines
].name
, line
, len
* U_SIZEOF_UCHAR
);
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
;
244 memcpy(newLines
, lines
, numLines
*sizeof(ULine
));
251 const UChar
* UPerfTest::getBuffer(int32_t& len
, UErrorCode
& status
){
252 if (U_FAILURE(status
)) {
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
);
262 UBool
UPerfTest::run(){
263 if(_remainingArgc
==1){
264 // Testing all methods
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
];
273 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
274 //fprintf(stdout, "\n%s:\n", name);
276 char* parameter
= strchr( name
, '@' );
282 res
= runTest( name
, parameter
);
283 if (!res
|| (execCount
<= 0)) {
284 fprintf(stdout
, "\n---ERROR: Test doesn't exist: %s!\n", name
);
291 UBool
UPerfTest::runTest(char* name
, char* par
){
296 pos
= strchr( name
, delim
); // check if name contains path (by looking for '/')
298 path
= pos
+1; // store subpath for calling subtest
299 *pos
= 0; // split into two strings
304 if (!name
|| (name
[0] == 0) || (strcmp(name
, "*") == 0)) {
305 rval
= runTestLoop( NULL
, NULL
);
307 }else if (strcmp( name
, "LIST" ) == 0) {
312 rval
= runTestLoop( name
, par
);
316 *pos
= delim
; // restore original value at pos
321 void UPerfTest::setPath( char* pathVal
)
323 this->path
= pathVal
;
326 // call individual tests, to be overridden to call implementations
327 UPerfFunction
* UPerfTest::runIndexedTest( int32_t /*index*/, UBool
/*exec*/, const char* & /*name*/, char* /*par*/ )
329 // to be overridden by a method like:
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;
337 fprintf(stderr
,"*** runIndexedTest needs to be overridden! ***");
342 UBool
UPerfTest::runTestLoop( char* testname
, char* par
)
348 UErrorCode status
= U_ZERO_ERROR
;
349 UPerfTest
* saveTest
= gTest
;
356 this->runIndexedTest( index
, FALSE
, name
);
357 if (!name
|| (name
[0] == 0))
360 run_this_test
= TRUE
;
362 run_this_test
= (UBool
) (strcmp( name
, testname
) == 0);
365 UPerfFunction
* testFunction
= this->runIndexedTest( index
, TRUE
, name
, par
);
368 if(testFunction
==NULL
){
369 fprintf(stderr
,"%s function returned NULL", name
);
372 ops
= testFunction
->getOperationsPerIteration();
374 fprintf(stderr
, "%s returned an illegal operations/iteration()\n", name
);
377 if(iterations
== 0) {
379 // Run for specified duration in seconds
381 fprintf(stdout
,"= %s calibrating %i seconds \n", name
, (int)n
);
384 //n *= 1000; // s => ms
385 //System.out.println("# " + meth.getName() + " " + n + " sec");
386 int32_t failsafe
= 1; // last resort for very fast methods
388 while (t
< (int)(n
* 0.9)) { // 90% is close enough
389 if (loops
== 0 || t
== 0) {
393 //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
394 loops
= (int)((double)n
/ t
* loops
+ 0.5);
396 fprintf(stderr
,"Unable to converge on desired duration");
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
));
411 double min_t
=1000000.0, sum_t
=0.0;
414 for(int32_t ps
=0; ps
< passes
; ps
++){
415 fprintf(stdout
,"= %s begin " ,name
);
418 fprintf(stdout
, "%i\n", (int)loops
);
420 fprintf(stdout
, "%i\n", (int)n
);
423 fprintf(stdout
, "\n");
425 t
= testFunction
->time(loops
, &status
);
426 if(U_FAILURE(status
)){
427 printf("Performance test failed with error: %s \n", u_errorName(status
));
434 events
= testFunction
->getEventsPerIteration();
435 //print info only in verbose mode
438 fprintf(stdout
, "= %s end: %f loops: %i operations: %li \n", name
, t
, (int)loops
, ops
);
440 fprintf(stdout
, "= %s end: %f loops: %i operations: %li events: %li\n", name
, t
, (int)loops
, ops
, events
);
444 fprintf(stdout
,"= %s end %f %i %li\n", name
, t
, (int)loops
, ops
);
446 fprintf(stdout
,"= %s end %f %i %li %li\n", name
, t
, (int)loops
, ops
, events
);
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
);
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
));
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
));
478 * Print a usage message for this test class.
480 void UPerfTest::usage( void )
483 if (_addUsage
!= NULL
) {
487 UBool save_verbose
= verbose
;
489 fprintf(stdout
,"Test names:\n");
490 fprintf(stdout
,"-----------\n");
493 const char* name
= NULL
;
495 this->runIndexedTest( index
, FALSE
, name
);
498 fprintf(stdout
, "%s\n", name
);
500 }while (name
&& (name
[0] != 0));
501 verbose
= save_verbose
;
507 void UPerfTest::setCaller( UPerfTest
* callingTest
)
509 caller
= callingTest
;
511 verbose
= caller
->verbose
;
515 UBool
UPerfTest::callTest( UPerfTest
& testToBeCalled
, char* par
)
517 execCount
--; // correct a previously assumed test-exec, as this only calls a subtest
518 testToBeCalled
.setCaller( this );
519 return testToBeCalled
.runTest( path
, par
);
522 UPerfTest::~UPerfTest(){
529 if(resolvedFileName
!=NULL
){
530 uprv_free(resolvedFileName
);
532 ucbuf_close(ucharBuf
);