Add a general purpose hashtable pattern matcher
[jimtcl.git] / bench.tcl
blob80f1506af1dead0cf559d4ebb5a4e24be87e62d7
1 set batchmode 0
2 set benchmarks {}
4 proc bench {title script} {
5 global benchmarks batchmode
7 set Title [string range "$title " 0 20]
9 set failed [catch {time $script} res]
10 if {$failed} {
11 if {!$batchmode} {puts "$Title - This test can't run on this interpreter"}
12 lappend benchmarks $title F
13 } else {
14 set t [expr [lindex $res 0] / 1000]
15 lappend benchmarks $title $t
16 set ts " $t"
17 set ts [string range $ts [expr {[string length $ts]-10}] end]
18 if {!$batchmode} {puts "$Title -$ts ms per iteration"}
20 catch { collect }
23 ### BUSY LOOP ##################################################################
25 proc whilebusyloop {} {
26 set i 0
27 while {$i < 1850000} {
28 set a 2
29 incr i
33 proc forbusyloop {} {
34 for {set i 0} {$i < 1850000} {incr i} {
35 set a 2
39 ### FIBONACCI ##################################################################
41 proc fibonacci {x} {
42 if {$x <= 1} {
43 expr 1
44 } else {
45 expr {[fibonacci [expr {$x-1}]] + [fibonacci [expr {$x-2}]]}
49 ### HEAPSORT ###################################################################
51 set IM 139968
52 set IA 3877
53 set IC 29573
55 set last 42
57 proc make_gen_random {} {
58 global IM IA IC
59 set params [list IM $IM IA $IA IC $IC]
60 set body [string map $params {
61 global last
62 expr {($max * [set last [expr {($last * IA + IC) % IM}]]) / IM}
64 proc gen_random {max} $body
67 proc heapsort {ra_name} {
68 upvar 1 $ra_name ra
69 set n [llength $ra]
70 set l [expr {$n / 2}]
71 set ir [expr {$n - 1}]
72 while 1 {
73 if {$l} {
74 set rra [lindex $ra [incr l -1]]
75 } else {
76 set rra [lindex $ra $ir]
77 lset ra $ir [lindex $ra 0]
78 if {[incr ir -1] == 0} {
79 lset ra 0 $rra
80 break
83 set i $l
84 set j [expr {(2 * $l) + 1}]
85 while {$j <= $ir} {
86 set tmp [lindex $ra $j]
87 if {$j < $ir} {
88 if {$tmp < [lindex $ra [expr {$j + 1}]]} {
89 set tmp [lindex $ra [incr j]]
92 if {$rra >= $tmp} {
93 break
95 lset ra $i $tmp
96 incr j [set i $j]
98 lset ra $i $rra
102 proc heapsort_main {} {
103 set n 6100
104 make_gen_random
106 set data {}
107 for {set i 1} {$i <= $n} {incr i} {
108 lappend data [gen_random 1.0]
110 heapsort data
113 ### SIEVE ######################################################################
115 proc sieve {num} {
116 while {$num > 0} {
117 incr num -1
118 set count 0
119 for {set i 2} {$i <= 8192} {incr i} {
120 set flags($i) 1
122 for {set i 2} {$i <= 8192} {incr i} {
123 if {$flags($i) == 1} {
124 # remove all multiples of prime: i
125 for {set k [expr {$i+$i}]} {$k <= 8192} {incr k $i} {
126 set flags($k) 0
128 incr count
132 return $count
135 proc sieve_dict {num} {
136 while {$num > 0} {
137 incr num -1
138 set count 0
139 for {set i 2} {$i <= 8192} {incr i} {
140 dict set flags $i 1
142 for {set i 2} {$i <= 8192} {incr i} {
143 if {[dict get $flags $i] == 1} {
144 # remove all multiples of prime: i
145 for {set k [expr {$i+$i}]} {$k <= 8192} {incr k $i} {
146 dict set flags $k 0
148 incr count
152 return $count
155 ### ARY ########################################################################
157 proc ary n {
158 for {set i 0} {$i < $n} {incr i} {
159 set x($i) $i
161 set last [expr {$n - 1}]
162 for {set j $last} {$j >= 0} {incr j -1} {
163 set y($j) $x($j)
167 proc ary_dict n {
168 for {set i 0} {$i < $n} {incr i} {
169 dict set x $i $i
171 set last [expr {$n - 1}]
172 for {set j $last} {$j >= 0} {incr j -1} {
173 dict set y $j $x($j)
177 ### REPEAT #####################################################################
179 proc repeat {n body} {
180 for {set i 0} {$i < $n} {incr i} {
181 uplevel 1 $body
185 proc use_repeat {} {
186 set x 0
187 repeat {1000000} {incr x}
190 ### UPVAR ######################################################################
192 proc myincr varname {
193 upvar 1 $varname x
194 incr x
197 proc upvartest {} {
198 set y 0
199 for {set x 0} {$x < 100000} {myincr x} {
200 myincr y
204 ### NESTED LOOPS ###############################################################
206 proc nestedloops {} {
207 set n 10
208 set x 0
209 incr n 1
210 set a $n
211 while {[incr a -1]} {
212 set b $n
213 while {[incr b -1]} {
214 set c $n
215 while {[incr c -1]} {
216 set d $n
217 while {[incr d -1]} {
218 set e $n
219 while {[incr e -1]} {
220 set f $n
221 while {[incr f -1]} {
222 incr x
231 ### ROTATE #####################################################################
233 proc rotate {count} {
234 set v 1
235 for {set n 0} {$n < $count} {incr n} {
236 set v [expr {$v <<< 1}]
240 ### DYNAMICALLY GENERATED CODE #################################################
242 proc dyncode {} {
243 for {set i 0} {$i < 100000} {incr i} {
244 set script "lappend foo $i"
245 eval $script
249 proc dyncode_list {} {
250 for {set i 0} {$i < 100000} {incr i} {
251 set script [list lappend foo $i]
252 eval $script
256 ### PI DIGITS ##################################################################
258 proc pi_digits {} {
259 set N 300
260 set LEN [expr {10*$N/3}]
261 set result ""
263 set a [string repeat " 2" $LEN]
264 set nines 0
265 set predigit 0
266 set nines {}
268 set i0 [expr {$LEN+1}]
269 set quot0 [expr {2*$LEN+1}]
270 for {set j 0} {$j<$N} {incr j} {
271 set q 0
272 set i $i0
273 set quot $quot0
274 set pos -1
275 foreach apos $a {
276 set x [expr {10*$apos + $q * [incr i -1]}]
277 lset a [incr pos] [expr {$x % [incr quot -2]}]
278 set q [expr {$x / $quot}]
280 lset a end [expr {$q % 10}]
281 set q [expr {$q / 10}]
282 if {$q < 8} {
283 append result $predigit $nines
284 set nines {}
285 set predigit $q
286 } elseif {$q == 9} {
287 append nines 9
288 } else {
289 append result [expr {$predigit+1}][string map {9 0} $nines]
290 set nines {}
291 set predigit 0
294 #puts $result$predigit
297 ### EXPAND #####################################################################
299 proc expand {} {
300 for {set i 0} {$i < 100000} {incr i} {
301 set a [list a b c d e f]
302 lappend b {*}$a
306 ### MINLOOPS ###################################################################
308 proc miniloops {} {
309 for {set i 0} {$i < 100000} {incr i} {
310 set sum 0
311 for {set j 0} {$j < 10} {incr j} {
312 # something of more or less real
313 incr sum $j
318 ### wiki.tcl.tk/8566 ###########################################################
320 # Internal procedure that indexes into the 2-dimensional array t,
321 # which corresponds to the sequence y, looking for the (i,j)th element.
323 proc Index { t y i j } {
324 set indx [expr { ([llength $y] + 1) * ($i + 1) + ($j + 1) }]
325 return [lindex $t $indx]
328 # Internal procedure that implements Levenshtein to derive the longest
329 # common subsequence of two lists x and y.
331 proc ComputeLCS { x y } {
332 set t [list]
333 for { set i -1 } { $i < [llength $y] } { incr i } {
334 lappend t 0
336 for { set i 0 } { $i < [llength $x] } { incr i } {
337 lappend t 0
338 for { set j 0 } { $j < [llength $y] } { incr j } {
339 if { [string equal [lindex $x $i] [lindex $y $j]] } {
340 set lastT [Index $t $y [expr { $i - 1 }] [expr {$j - 1}]]
341 set nextT [expr {$lastT + 1}]
342 } else {
343 set lastT1 [Index $t $y $i [expr { $j - 1 }]]
344 set lastT2 [Index $t $y [expr { $i - 1 }] $j]
345 if { $lastT1 > $lastT2 } {
346 set nextT $lastT1
347 } else {
348 set nextT $lastT2
351 lappend t $nextT
354 return $t
357 # Internal procedure that traces through the array built by ComputeLCS
358 # and finds a longest common subsequence -- specifically, the one that
359 # is lexicographically first.
361 proc TraceLCS { t x y } {
362 set trace {}
363 set i [expr { [llength $x] - 1 }]
364 set j [expr { [llength $y] - 1 }]
365 set k [expr { [Index $t $y $i $j] - 1 }]
366 while { $i >= 0 && $j >= 0 } {
367 set im1 [expr { $i - 1 }]
368 set jm1 [expr { $j - 1 }]
369 if { [Index $t $y $i $j] == [Index $t $y $im1 $jm1] + 1
370 && [string equal [lindex $x $i] [lindex $y $j]] } {
371 lappend trace xy [list $i $j]
372 set i $im1
373 set j $jm1
374 } elseif { [Index $t $y $im1 $j] > [Index $t $y $i $jm1] } {
375 lappend trace x $i
376 set i $im1
377 } else {
378 lappend trace y $j
379 set j $jm1
382 while { $i >= 0 } {
383 lappend trace x $i
384 incr i -1
386 while { $j >= 0 } {
387 lappend trace y $j
388 incr j -1
390 return $trace
393 # list::longestCommonSubsequence::compare --
395 # Compare two lists for the longest common subsequence
397 # Arguments:
398 # x, y - Two lists of strings to compare
399 # matched - Callback to execute on matched elements, see below
400 # unmatchedX - Callback to execute on unmatched elements from the
401 # first list, see below.
402 # unmatchedY - Callback to execute on unmatched elements from the
403 # second list, see below.
405 # Results:
406 # None.
408 # Side effects:
409 # Whatever the callbacks do.
411 # The 'compare' procedure compares the two lists of strings, x and y.
412 # It finds a longest common subsequence between the two. It then walks
413 # the lists in order and makes the following callbacks:
415 # For an element that is common to both lists, it appends the index in
416 # the first list, the index in the second list, and the string value of
417 # the element as three parameters to the 'matched' callback, and executes
418 # the result.
420 # For an element that is in the first list but not the second, it appends
421 # the index in the first list and the string value of the element as two
422 # parameters to the 'unmatchedX' callback and executes the result.
424 # For an element that is in the second list but not the first, it appends
425 # the index in the second list and the string value of the element as two
426 # parameters to the 'unmatchedY' callback and executes the result.
428 proc compare { x y
429 matched
430 unmatchedX unmatchedY } {
431 set t [ComputeLCS $x $y]
432 set trace [TraceLCS $t $x $y]
433 set i [llength $trace]
434 while { $i > 0 } {
435 set indices [lindex $trace [incr i -1]]
436 set type [lindex $trace [incr i -1]]
437 switch -exact -- $type {
438 xy {
439 set c $matched
440 eval lappend c $indices
441 lappend c [lindex $x [lindex $indices 0]]
442 uplevel 1 $c
445 set c $unmatchedX
446 lappend c $indices
447 lappend c [lindex $x $indices]
448 uplevel 1 $c
451 set c $unmatchedY
452 lappend c $indices
453 lappend c [lindex $y $indices]
454 uplevel 1 $c
458 return
461 proc umx { index value } {
462 global lastx
463 global xlines
464 append xlines "< " $value \n
465 set lastx $index
468 proc umy { index value } {
469 global lasty
470 global ylines
471 append ylines "> " $value \n
472 set lasty $index
475 proc matched { index1 index2 value } {
476 global lastx
477 global lasty
478 global xlines
479 global ylines
480 if { [info exists lastx] && [info exists lasty] } {
481 #puts "[expr { $lastx + 1 }],${index1}c[expr {$lasty + 1 }],${index2}"
482 #puts -nonewline $xlines
483 #puts "----"
484 #puts -nonewline $ylines
485 } elseif { [info exists lastx] } {
486 #puts "[expr { $lastx + 1 }],${index1}d${index2}"
487 #puts -nonewline $xlines
488 } elseif { [info exists lasty] } {
489 #puts "${index1}a[expr {$lasty + 1 }],${index2}"
490 #puts -nonewline $ylines
492 catch { unset lastx }
493 catch { unset xlines }
494 catch { unset lasty }
495 catch { unset ylines }
498 # Really, we should read the first file in like this:
499 # set f0 [open [lindex $argv 0] r]
500 # set x [split [read $f0] \n]
501 # close $f0
502 # But I'll just provide some sample lines:
504 proc commonsub_test {} {
505 set x {}
506 for { set i 0 } { $i < 20 } { incr i } {
507 lappend x a r a d e d a b r a x
510 # The second file, too, should be read in like this:
511 # set f1 [open [lindex $argv 1] r]
512 # set y [split [read $f1] \n]
513 # close $f1
514 # Once again, I'll just do some sample lines.
516 set y {}
517 for { set i 0 } { $i < 20 } { incr i } {
518 lappend y a b r a c a d a b r a
521 compare $x $y matched umx umy
522 matched [llength $x] [llength $y] {}
525 ### MANDEL #####################################################################
527 proc mandel {xres yres infx infy supx supy} {
528 set incremx [expr {double($supx-$infx)/$xres}]
529 set incremy [expr {double($supy-$infy)/$yres}]
531 for {set j 0} {$j < $yres} {incr j} {
532 set cim [expr {$infy+$incremy*$j}]
533 set line {}
534 for {set i 0} {$i < $xres} {incr i} {
535 set counter 0
536 set zim 0
537 set zre 0
538 set cre [expr {$infx+$incremx*$i}]
539 while {$counter < 255} {
540 set dam [expr {$zre*$zre-$zim*$zim+$cre}]
541 set zim [expr {2*$zim*$zre+$cim}]
542 set zre $dam
543 if {$zre*$zre+$zim*$zim > 4} break
544 incr counter
546 # output pixel $i $j
551 ### RUN ALL ####################################################################
553 if {[string compare [lindex $argv 0] "-batch"] == 0} {
554 set batchmode 1
555 set argv [lrange $argv 1 end]
557 set ver [lindex $argv 0]
559 bench {[while] busy loop} {whilebusyloop}
560 bench {[for] busy loop} {forbusyloop}
561 bench {mini loops} {miniloops}
562 bench {fibonacci(25)} {fibonacci 25}
563 bench {heapsort} {heapsort_main}
564 bench {sieve} {sieve 10}
565 bench {sieve [dict]} {sieve_dict 10}
566 bench {ary} {ary 100000}
567 bench {ary [dict]} {ary_dict 100000}
568 bench {repeat} {use_repeat}
569 bench {upvar} {upvartest}
570 bench {nested loops} {nestedloops}
571 bench {rotate} {rotate 100000}
572 bench {dynamic code} {dyncode}
573 bench {dynamic code (list)} {dyncode_list}
574 bench {PI digits} {pi_digits}
575 bench {expand} {expand}
576 bench {wiki.tcl.tk/8566} {commonsub_test}
577 bench {mandel} {mandel 60 60 -2 -1.5 1 1.5}
579 if {$batchmode} {
580 if {$ver == ""} {
581 if {[catch {info patchlevel} ver]} {
582 set ver Jim[info version]
585 puts [list $ver $benchmarks]