#!/bin/sh # the next line restarts using tclsh \ exec tclsh "$0" ${1+"$@"} # # This program runs performance testing on sqlite3.c. Usage: set usage {USAGE: speedtest.tcl sqlite3.c x1.txt trunk.txt -Os -DSQLITE_ENABLE_STAT4 | | | `-----------------------' File to test ----' | | | | | `- options Output filename --------' | `--- optional prior output to diff Do a cache-grind performance analysis of the sqlite3.c file named and write the results into the output file. The ".txt" is appended to the output file (and diff-file) name if it is not already present. If the diff-file is specified then show a diff from the diff-file to the new output. Other options include: CC=... Specify an alternative C compiler. Default is "gcc". -D... -D and -O options are passed through to the C compiler. --dryrun Show what would happen but don't do anything. --help Show this help screen. --lean "Lean" mode. --lookaside N SZ Lookahead uses N slots of SZ bytes each. --pagesize N Use N as the page size. --quiet | -q "Quite". Put results in file but don't pop up editor --size N Change the test size. 100 means 100%. Default: 5. --testset TEST Specify the specific testset to use. The default is "mix1". Other options include: "main", "json", "cte", "orm", "fp", "rtree". } set srcfile {} set outfile {} set difffile {} set cflags {-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_THREADSAFE=0} set cc gcc set testset mix1 set dryrun 0 set quiet 0 set speedtestflags {--shrink-memory --reprepare --stats --heap 40000000 64} lappend speedtestflags --journal wal --size 5 for {set i 0} {$i<[llength $argv]} {incr i} { set arg [lindex $argv $i] if {[string index $arg 0]=="-"} { switch -- $arg { -pagesize - --pagesize { lappend speedtestflags --pagesize incr i lappend speedtestflags [lindex $argv $i] } -lookaside - --lookaside { lappend speedtestflags --lookaside incr i lappend speedtestflags [lindex $argv $i] incr i lappend speedtestflags [lindex $argv $i] } -lean - --lean { lappend cflags \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 \ -DSQLITE_MAX_EXPR_DEPTH=1 \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_USE_ALLOCA } -testset - --testset { incr i set testset [lindex $argv $i] } -size - --size { incr i set newsize [lindex $argv $i] if {$newsize<1} {set newsize 1} set speedtestflags \ [regsub {.-size \d+} $speedtestflags "-size $newsize"] } -n - -dryrun - --dryrun { set dryrun 1 } -? - -help - --help { puts $usage exit 0 } -q - -quiet - --quiet { set quiet 1 } default { lappend cflags $arg } } continue } if {[string match CC=* $arg]} { set cc [lrange $arg 3 end] continue } if {[string match *.c $arg]} { if {$srcfile!=""} { puts stderr "multiple source files: $srcfile $arg" exit 1 } set srcfile $arg continue } if {[lsearch {main cte rtree orm fp json parsenumber mix1} $arg]>=0} { set testset $arg continue } if {$outfile==""} { set outfile $arg continue } if {$difffile==""} { set difffile $arg continue } puts stderr "unknown option: \"$arg\". Use --help for more info." exit 1 } if {[lsearch -glob $cflags -O*]<0} { lappend cflags -Os } if {[lsearch -glob $cflags -DSQLITE_ENABLE_MEMSYS*]<0} { lappend cflags -DSQLITE_ENABLE_MEMSYS5 } if {[lsearch -glob $cflags -DSQLITE_ENABLE_RTREE*]<0} { lappend cflags -DSQLITE_ENABLE_RTREE } if {$srcfile==""} { puts stderr "no sqlite3.c source file specified" exit 1 } if {![file readable $srcfile]} { puts stderr "source file \"$srcfile\" does not exist" exit 1 } if {$outfile==""} { puts stderr "no output file specified" exit 1 } if {![string match *.* [file tail $outfile]]} { append outfile .txt } if {$difffile!=""} { if {![file exists $difffile]} { if {[file exists $difffile.txt]} { append difffile .txt } else { puts stderr "No such file: \"$difffile\"" exit 1 } } } set cccmd [list $cc -g] lappend cccmd -I[file dir $srcfile] lappend cccmd {*}[lsort $cflags] lappend cccmd [file dir $argv0]/speedtest1.c lappend cccmd $srcfile lappend cccmd -o speedtest1 puts $cccmd if {!$dryrun} { exec {*}$cccmd } lappend speedtestflags --testset $testset set stcmd [list valgrind --tool=cachegrind ./speedtest1 {*}$speedtestflags] lappend stcmd speedtest1.db lappend stcmd >valgrind-out.txt 2>valgrind-err.txt puts $stcmd if {!$dryrun} { foreach file {speedtest1.db speedtest1.db-journal speedtest1.db-wal speedtest1.db-shm} { if {[file exists $file]} {file delete $file} } exec {*}$stcmd } set maxmtime 0 set cgfile {} foreach cgout [glob -nocomplain cachegrind.out.*] { if {[file mtime $cgout]>$maxmtime} { set cgfile $cgout set maxmtime [file mtime $cgfile] } } if {$cgfile==""} { puts "no cachegrind output" exit 1 } ############# Process the cachegrind.out.# file ########################## set fd [open $outfile wb] set in [open "|cg_annotate --show=Ir --auto=yes --context=40 $cgfile" r] set dest ! set out(!) {} set linenum 0 set cntlines 0 ;# true to remember cycle counts on each line set seenSqlite3 0 ;# true if we have seen the sqlite3.c file while {![eof $in]} { set line [string map {\t { }} [gets $in]] if {[regexp {^-- Auto-annotated source: (.*)} $line all name]} { set dest $name if {[string match */sqlite3.c $dest]} { set cntlines 1 set seenSqlite3 1 } else { set cntlines 0 } } elseif {[regexp {^-- line (\d+) ------} $line all ln]} { set line [lreplace $line 2 2 {#}] set linenum [expr {$ln-1}] } elseif {[regexp {^The following files chosen for } $line]} { set dest ! } append out($dest) $line\n if {$cntlines} { incr linenum if {[regexp {^ *([0-9,]+) } $line all x]} { set x [string map {, {}} $x] set cycles($linenum) $x } } } foreach x [lsort [array names out]] { puts $fd $out($x) } # If the sqlite3.c file has been seen, then output a summary of the # cycle counts for each file that went into making up sqlite3.c # if {$seenSqlite3} { close $in set in [open sqlite3.c] set linenum 0 set fn sqlite3.c set pattern1 {^/\*+ Begin file ([^ ]+) \*} set pattern2 {^/\*+ Continuing where we left off in ([^ ]+) \*} while {![eof $in]} { set line [gets $in] incr linenum if {[regexp $pattern1 $line all newfn]} { set fn $newfn } elseif {[regexp $pattern2 $line all newfn]} { set fn $newfn } elseif {[info exists cycles($linenum)]} { incr fcycles($fn) $cycles($linenum) } } close $in puts $fd \ {**********************************************************************} set lx {} set sum 0 foreach {fn cnt} [array get fcycles] { lappend lx [list $cnt $fn] incr sum $cnt } puts $fd [format {%20s %14d %8.3f%%} TOTAL $sum 100] foreach entry [lsort -index 0 -integer -decreasing $lx] { foreach {cnt fn} $entry break puts $fd [format {%20s %14d %8.3f%%} $fn $cnt [expr {$cnt*100.0/$sum}]] } } puts $fd "Executable size:" close $fd exec size speedtest1 >>$outfile # # Processed cachegrind output should now be in the $outfile ############################################################################# if {$quiet} { # Skip this last part of popping up a GUI viewer } elseif {$difffile!=""} { set fossilcmd {fossil xdiff --tk -c 20} lappend fossilcmd $difffile lappend fossilcmd $outfile lappend fossilcmd & puts $fossilcmd if {!$dryrun} { exec {*}$fossilcmd } } else { if {!$dryrun} { exec open $outfile } }