]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/test/perf/dateformatperf.pl
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / test / perf / dateformatperf.pl
1 #!/usr/local/bin/perl\r
2 # *******************************************************************************\r
3 # * Copyright (C) 2002-2007 International Business Machines Corporation and     *\r
4 # * others. All Rights Reserved.                                                *\r
5 # *******************************************************************************\r
6 \r
7 use strict;\r
8 \r
9 # Assume we are running within the icu4j root directory\r
10 use lib 'src/com/ibm/icu/dev/test/perf';\r
11 use Dataset;\r
12 \r
13 #---------------------------------------------------------------------\r
14 # Test class\r
15 my $TESTCLASS = 'com.ibm.icu.dev.test.perf.DateFormatPerformanceTest';\r
16 \r
17 # Methods to be tested.  Each pair represents a test method and\r
18 # a baseline method which is used for comparison.\r
19 my @METHODS  = (\r
20                  ['TestJDKConstruction',     'TestICUConstruction'],\r
21                  ['TestJDKParse',            'TestICUParse'],\r
22                  ['TestJDKFormat',           'TestICUFormat']\r
23                );\r
24 # Patterns which define the set of characters used for testing.\r
25 my @OPTIONS = (\r
26 #                 locale    pattern              date string\r
27                 [ "en_US",  "dddd MMM yyyy",     "15 Jan 2007"],\r
28                 [ "sw_KE",  "dddd MMM yyyy",     "15 Jan 2007"],\r
29                 [ "en_US",  "HH:mm",             "13:13"],\r
30                 [ "en_US",  "HH:mm zzzz",        "13:13 Pacific Standard Time"],\r
31                 [ "en_US",  "HH:mm z",           "13:13 PST"],\r
32                 [ "en_US",  "HH:mm Z",           "13:13 -0800"],\r
33               );\r
34 \r
35 my $THREADS;        # number of threads (input from command-line args)\r
36 my $CALIBRATE = 2;  # duration in seconds for initial calibration\r
37 my $DURATION  = 10; # duration in seconds for each pass\r
38 my $NUMPASSES = 4;  # number of passes.  If > 1 then the first pass\r
39                     # is discarded as a JIT warm-up pass.\r
40 \r
41 my $TABLEATTR = 'BORDER="1" CELLPADDING="4" CELLSPACING="0"';\r
42 \r
43 my $PLUS_MINUS = "±";\r
44 \r
45 if ($NUMPASSES < 3) {\r
46     die "Need at least 3 passes.  One is discarded (JIT warmup) and need two to have 1 degree of freedom (t distribution).";\r
47 }\r
48 \r
49 my $OUT; # see out()\r
50 \r
51 # run all tests with the specified number of threads from command-line input\r
52 # (if there is no arguments, use $THREADS = 1)\r
53 foreach my $arg ($#ARGV >= 0 ? @ARGV : "1") {\r
54   $THREADS = $arg;\r
55   main();\r
56 }\r
57 \r
58 \r
59 #---------------------------------------------------------------------\r
60 # ...\r
61 sub main {\r
62     my $date = localtime;\r
63     my $threads = ($THREADS > 1) ? "($THREADS threads)" : "";\r
64     my $title = "ICU4J Performance Test $threads $date";\r
65 \r
66     my $html = $date;\r
67     $html =~ s/://g; # ':' illegal\r
68     $html =~ s/\s*\d+$//; # delete year\r
69     $html =~ s/^\w+\s*//; # delete dow\r
70     $html = "perf $html.html";\r
71 \r
72     open(HTML,">$html") or die "Can't write to $html: $!";\r
73 \r
74     print HTML <<EOF;\r
75 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\r
76    "http://www.w3.org/TR/html4/strict.dtd">\r
77 <HTML>\r
78    <HEAD>\r
79       <TITLE>$title</TITLE>\r
80    </HEAD>\r
81    <BODY>\r
82 EOF\r
83     print HTML "<H1>$title</H1>\n";\r
84 \r
85     print HTML "<H2>$TESTCLASS</H2>\n";\r
86 \r
87     my $raw = "";\r
88 \r
89     for my $methodPair (@METHODS) {\r
90 \r
91         my $testMethod = $methodPair->[0];\r
92         my $baselineMethod = $methodPair->[1];\r
93 \r
94         print HTML "<P><TABLE $TABLEATTR><TR><TD>\n";\r
95         print HTML "<P><B>$testMethod vs. $baselineMethod</B></P>\n";\r
96         \r
97         print HTML "<P><TABLE $TABLEATTR BGCOLOR=\"#CCFFFF\">\n";\r
98         print HTML "<TR><TD>Options</TD><TD>$testMethod</TD>";\r
99         print HTML "<TD>$baselineMethod</TD><TD>Ratio</TD></TR>\n";\r
100 \r
101         $OUT = '';\r
102 \r
103         for my $pat (@OPTIONS) {\r
104             print HTML "<TR><TD>@$pat[0], \"@$pat[1]\", \"@$pat[2]\"</TD>\n";\r
105 \r
106             out("<P><TABLE $TABLEATTR WIDTH=\"100%\">");\r
107 \r
108             # measure the test method\r
109             out("<TR><TD>");\r
110             print "\n$testMethod [@$pat]\n";\r
111             my $t = measure2($testMethod, $pat, -$DURATION);\r
112             out("</TD></TR>");\r
113             print HTML "<TD>", formatSeconds(4, $t->getMean(), $t->getError);\r
114             print HTML "/event</TD>\n";\r
115 \r
116             # measure baseline method\r
117             out("<TR><TD>");\r
118             print "\n$baselineMethod [@$pat]\n";\r
119             my $b = measure2($baselineMethod, $pat, -$DURATION);\r
120             out("</TD></TR>");\r
121             print HTML "<TD>", formatSeconds(4, $b->getMean(), $t->getError);\r
122             print HTML "/event</TD>\n";\r
123 \r
124             out("</TABLE></P>");\r
125 \r
126             # output ratio\r
127             my $r = $t->divide($b);\r
128             my $mean = $r->getMean() - 1;\r
129             my $color = $mean < 0 ? "RED" : "BLACK";\r
130             print HTML "<TD><B><FONT COLOR=\"$color\">", formatPercent(3, $mean, $r->getError);\r
131             print HTML "</FONT></B></TD></TR>\n";\r
132         }\r
133 \r
134         print HTML "</TABLE></P>\n";\r
135 \r
136         print HTML "<P>Raw data:</P>\n";\r
137         print HTML $OUT;\r
138         print HTML "</TABLE></P>\n";\r
139     }\r
140 \r
141     print HTML <<EOF;\r
142    </BODY>\r
143 </HTML>\r
144 EOF\r
145     close(HTML) or die "Can't close $html: $!";\r
146 }\r
147 \r
148 #---------------------------------------------------------------------\r
149 # Append text to the global variable $OUT\r
150 sub out {\r
151     $OUT .= join('', @_);\r
152 }\r
153 \r
154 #---------------------------------------------------------------------\r
155 # Append text to the global variable $OUT\r
156 sub outln {\r
157     $OUT .= join('', @_) . "\n";\r
158 }\r
159 \r
160 #---------------------------------------------------------------------\r
161 # Measure a given test method with a give test pattern using the\r
162 # global run parameters.\r
163 #\r
164 # @param the method to run\r
165 # @param the pattern defining characters to test\r
166 # @param if >0 then the number of iterations per pass.  If <0 then\r
167 #        (negative of) the number of seconds per pass.\r
168 #\r
169 # @return a Dataset object, scaled by iterations per pass and\r
170 #         events per iteration, to give time per event\r
171 #\r
172 sub measure2 {\r
173     my @data = measure1(@_);\r
174     my $iterPerPass = shift(@data);\r
175     my $eventPerIter = shift(@data);\r
176 \r
177     shift(@data) if (@data > 1); # discard first run\r
178 \r
179     my $ds = Dataset->new(@data);\r
180     $ds->setScale(1.0e-3 / ($iterPerPass * $eventPerIter));\r
181     $ds;\r
182 }\r
183 \r
184 #---------------------------------------------------------------------\r
185 # Measure a given test method with a give test pattern using the\r
186 # global run parameters.\r
187 #\r
188 # @param the method to run\r
189 # @param the pattern defining characters to test\r
190 # @param if >0 then the number of iterations per pass.  If <0 then\r
191 #        (negative of) the number of seconds per pass.\r
192 #\r
193 # @return array of:\r
194 #         [0] iterations per pass\r
195 #         [1] events per iteration\r
196 #         [2..] ms reported for each pass, in order\r
197 #\r
198 sub measure1 {\r
199     my $method = shift;\r
200     my $pat = shift;\r
201     my $iterCount = shift; # actually might be -seconds/pass\r
202 \r
203     out("<P>Measuring $method for input file @$pat[0] for encoding @$pat[2] , ");\r
204     if ($iterCount > 0) {\r
205         out("$iterCount iterations/pass, $NUMPASSES passes</P>\n");\r
206     } else {\r
207         out(-$iterCount, " seconds/pass, $NUMPASSES passes</P>\n");\r
208     }\r
209 \r
210     # is $iterCount actually -seconds/pass?\r
211     if ($iterCount < 0) {\r
212 \r
213         # calibrate: estimate ms/iteration\r
214         print "Calibrating...";\r
215         my @t = callJava($method, $pat, -$CALIBRATE, 1);\r
216         print "done.\n";\r
217 \r
218         my @data = split(/\s+/, $t[0]->[2]);\r
219         $data[0] *= 1.0e+3;\r
220 \r
221         my $timePerIter = 1.0e-3 * $data[0] / $data[1];\r
222         \r
223         # determine iterations/pass\r
224         $iterCount = int(-$iterCount / $timePerIter + 0.5);\r
225         \r
226         out("<P>Calibration pass ($CALIBRATE sec): ");\r
227         out("$data[0] ms, ");\r
228         out("$data[1] iterations = ");\r
229         out(formatSeconds(4, $timePerIter), "/iteration<BR>\n");\r
230     }\r
231     \r
232     # run passes\r
233     print "Measuring $iterCount iterations x $NUMPASSES passes...";\r
234     my @t = callJava($method, $pat, $iterCount, $NUMPASSES);\r
235     print "done.\n";\r
236     my @ms = ();\r
237     my @b; # scratch\r
238     for my $a (@t) {\r
239         # $a->[0]: method name, corresponds to $method\r
240         # $a->[1]: 'begin' data, == $iterCount\r
241         # $a->[2]: 'end' data, of the form <ms> <loops> <eventsPerIter>\r
242         # $a->[3...]: gc messages from JVM during pass\r
243         @b = split(/\s+/, $a->[2]);\r
244         push(@ms, $b[0] * 1.0e+3);\r
245     }\r
246     my $eventsPerIter = $b[2];\r
247 \r
248     out("Iterations per pass: $iterCount<BR>\n");\r
249     out("Events per iteration: $eventsPerIter<BR>\n");\r
250 \r
251     my @ms_str = @ms;\r
252     $ms_str[0] .= " (discarded)" if (@ms_str > 1);\r
253     out("Raw times (ms/pass): ", join(", ", @ms_str), "<BR>\n");\r
254 \r
255     ($iterCount, $eventsPerIter, @ms);\r
256 }\r
257 \r
258 #---------------------------------------------------------------------\r
259 # Invoke java to run $TESTCLASS, passing it the given parameters.\r
260 #\r
261 # @param the method to run\r
262 # @param the number of iterations, or if negative, the duration\r
263 #        in seconds.  If more than on pass is desired, pass in\r
264 #        a string, e.g., "100 100 100".\r
265 # @param the pattern defining characters to test\r
266 #\r
267 # @return an array of results.  Each result is an array REF\r
268 #         describing one pass.  The array REF contains:\r
269 #         ->[0]: The method name as reported\r
270 #         ->[1]: The params on the '= <meth> begin ...' line\r
271 #         ->[2]: The params on the '= <meth> end ...' line\r
272 #         ->[3..]: GC messages from the JVM, if any\r
273 #\r
274 sub callJava {\r
275     my $method = shift;\r
276     my $pat = shift;\r
277     my $n = shift;\r
278     my $passes = shift;\r
279     \r
280     my $n = ($n < 0) ? "-t ".(-$n) : "-i ".$n;\r
281     \r
282     my $cmd = "java -classpath classes $TESTCLASS $method $n -p $passes -L @$pat[0] \"@$pat[1]\" \"@$pat[2]\" -r $THREADS";\r
283     print "[$cmd]\n"; # for debugging\r
284     open(PIPE, "$cmd|") or die "Can't run \"$cmd\"";\r
285     my @out;\r
286     while (<PIPE>) {\r
287         push(@out, $_);\r
288     }\r
289     close(PIPE) or die "Java failed: \"$cmd\"";\r
290 \r
291     @out = grep(!/^\#/, @out);  # filter out comments\r
292 \r
293     #print "[", join("\n", @out), "]\n";\r
294 \r
295     my @results;\r
296     my $method = '';\r
297     my $data = [];\r
298     foreach (@out) {\r
299         next unless (/\S/);\r
300 \r
301         if (/^=\s*(\w+)\s*(\w+)\s*(.*)/) {\r
302             my ($m, $state, $d) = ($1, $2, $3);\r
303             #print "$_ => [[$m $state $data]]\n";\r
304             if ($state eq 'begin') {\r
305                 die "$method was begun but not finished" if ($method);\r
306                 $method = $m;\r
307                 push(@$data, $d);\r
308                 push(@$data, ''); # placeholder for end data\r
309             } elsif ($state eq 'end') {\r
310                 if ($m ne $method) {\r
311                     die "$method end does not match: $_";\r
312                 }\r
313                 $data->[1] = $d; # insert end data at [1]\r
314                 #print "#$method:", join(";",@$data), "\n";\r
315                 unshift(@$data, $method); # add method to start\r
316 \r
317                 push(@results, $data);\r
318                 $method = '';\r
319                 $data = [];\r
320             } else {\r
321                 die "Can't parse: $_";\r
322             }\r
323         }\r
324 \r
325         elsif (/^\[/) {\r
326             if ($method) {\r
327                 push(@$data, $_);\r
328             } else {\r
329                 # ignore extraneous GC notices\r
330             }\r
331         }\r
332 \r
333         else {\r
334             die "Can't parse: $_";\r
335         }\r
336     }\r
337 \r
338     die "$method was begun but not finished" if ($method);\r
339 \r
340     @results;\r
341 }\r
342 \r
343 #|#---------------------------------------------------------------------\r
344 #|# Format a confidence interval, as given by a Dataset.  Output is as\r
345 #|# as follows:\r
346 #|#   241.23 - 241.98 => 241.5 +/- 0.3\r
347 #|#   241.2 - 243.8 => 242 +/- 1\r
348 #|#   211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20\r
349 #|#   220.3 - 234.3 => 227 +/- 7\r
350 #|#   220.3 - 300.3 => 260 +/- 40\r
351 #|#   220.3 - 1000 => 610 +/- 390 or? 600 +/- 400\r
352 #|#   0.022 - 0.024 => 0.023 +/- 0.001\r
353 #|#   0.022 - 0.032 => 0.027 +/- 0.005\r
354 #|#   0.022 - 1.000 => 0.5 +/- 0.5\r
355 #|# In other words, take one significant digit of the error value and\r
356 #|# display the mean to the same precision.\r
357 #|sub formatDataset {\r
358 #|    my $ds = shift;\r
359 #|    my $lower = $ds->getMean() - $ds->getError();\r
360 #|    my $upper = $ds->getMean() + $ds->getError();\r
361 #|    my $scale = 0;\r
362 #|    # Find how many initial digits are the same\r
363 #|    while ($lower < 1 ||\r
364 #|           int($lower) == int($upper)) {\r
365 #|        $lower *= 10;\r
366 #|        $upper *= 10;\r
367 #|        $scale++;\r
368 #|    }\r
369 #|    while ($lower >= 10 &&\r
370 #|           int($lower) == int($upper)) {\r
371 #|        $lower /= 10;\r
372 #|        $upper /= 10;\r
373 #|        $scale--;\r
374 #|    }\r
375 #|}\r
376 \r
377 #---------------------------------------------------------------------\r
378 # Format a number, optionally with a +/- delta, to n significant\r
379 # digits.\r
380 #\r
381 # @param significant digit, a value >= 1\r
382 # @param multiplier\r
383 # @param time in seconds to be formatted\r
384 # @optional delta in seconds\r
385 #\r
386 # @return string of the form "23" or "23 +/- 10".\r
387 #\r
388 sub formatNumber {\r
389     my $sigdig = shift;\r
390     my $mult = shift;\r
391     my $a = shift;\r
392     my $delta = shift; # may be undef\r
393     \r
394     my $result = formatSigDig($sigdig, $a*$mult);\r
395     if (defined($delta)) {\r
396         my $d = formatSigDig($sigdig, $delta*$mult);\r
397         # restrict PRECISION of delta to that of main number\r
398         if ($result =~ /\.(\d+)/) {\r
399             # TODO make this work for values with all significant\r
400             # digits to the left of the decimal, e.g., 1234000.\r
401 \r
402             # TODO the other thing wrong with this is that it\r
403             # isn't rounding the $delta properly.  Have to put\r
404             # this logic into formatSigDig().\r
405             my $x = length($1);\r
406             $d =~ s/\.(\d{$x})\d+/.$1/;\r
407         }\r
408         $result .= " $PLUS_MINUS " . $d;\r
409     }\r
410     $result;\r
411 }\r
412 \r
413 #---------------------------------------------------------------------\r
414 # Format a time, optionally with a +/- delta, to n significant\r
415 # digits.\r
416 #\r
417 # @param significant digit, a value >= 1\r
418 # @param time in seconds to be formatted\r
419 # @optional delta in seconds\r
420 #\r
421 # @return string of the form "23 ms" or "23 +/- 10 ms".\r
422 #\r
423 sub formatSeconds {\r
424     my $sigdig = shift;\r
425     my $a = shift;\r
426     my $delta = shift; # may be undef\r
427 \r
428     my @MULT = (1   , 1e3,  1e6,  1e9);\r
429     my @SUFF = ('s' , 'ms', 'us', 'ns');\r
430 \r
431     # Determine our scale\r
432     my $i = 0;\r
433     ++$i while ($a*$MULT[$i] < 1 && $i < @MULT);\r
434     \r
435     formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];\r
436 }\r
437 \r
438 #---------------------------------------------------------------------\r
439 # Format a percentage, optionally with a +/- delta, to n significant\r
440 # digits.\r
441 #\r
442 # @param significant digit, a value >= 1\r
443 # @param value to be formatted, as a fraction, e.g. 0.5 for 50%\r
444 # @optional delta, as a fraction\r
445 #\r
446 # @return string of the form "23 %" or "23 +/- 10 %".\r
447 #\r
448 sub formatPercent {\r
449     my $sigdig = shift;\r
450     my $a = shift;\r
451     my $delta = shift; # may be undef\r
452     \r
453     formatNumber($sigdig, 100, $a, $delta) . ' %';\r
454 }\r
455 \r
456 #---------------------------------------------------------------------\r
457 # Format a number to n significant digits without using exponential\r
458 # notation.\r
459 #\r
460 # @param significant digit, a value >= 1\r
461 # @param number to be formatted\r
462 #\r
463 # @return string of the form "1234" "12.34" or "0.001234".  If\r
464 #         number was negative, prefixed by '-'.\r
465 #\r
466 sub formatSigDig {\r
467     my $n = shift() - 1;\r
468     my $a = shift;\r
469 \r
470     local $_ = sprintf("%.${n}e", $a);\r
471     my $sign = (s/^-//) ? '-' : '';\r
472 \r
473     my $a_e;\r
474     my $result;\r
475     if (/^(\d)\.(\d+)e([-+]\d+)$/) {\r
476         my ($d, $dn, $e) = ($1, $2, $3);\r
477         $a_e = $e;\r
478         $d .= $dn;\r
479         $e++;\r
480         $d .= '0' while ($e > length($d));\r
481         while ($e < 1) {\r
482             $e++;\r
483             $d = '0' . $d;\r
484         }\r
485         if ($e == length($d)) {\r
486             $result = $sign . $d;\r
487         } else {\r
488             $result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);\r
489         }\r
490     } else {\r
491         die "Can't parse $_";\r
492     }\r
493     $result;\r
494 }\r
495 \r
496 #eof\r