Profiling
Gerbil ships with a basic profiler tool, gerbil prof
or just
gxprof
. The tool can profile dynamically compiled executable
modules, by running them with an interrupt handler that samples the
current continuation on every scheduler heartbeat.
Once a profile sample file has been generated by running gxprof
with
your module, you can analyze the samples for some (hopefully)
insightful statistics.
Invocation
$ gerbil prof -h
gxprof: The Gerbil profiler
Usage: gxprof [option ...] [<module>] <module-args> ...
Options:
-o --output <output> gxprof output file [default: gxprof.out]
--heartbeat <heartbeat> heartbeat interval for sampling, in seconds [default: .001]
-h --help display help
Arguments:
module dynamic executable module to run; analyze an existing output file if omitted [default: #f]
module-args arguments to pass to the executable module's main
So basically gxprof :your/executable-module arg ...
will run the
main function in :your/executable-module
with the given arguments. The function
is run to completion, at which point the profiler writes the captured
continuation samples in gxprof.out in the current directory.
The contents of the file is a list of lists that contain the names of the continuation creator for each frame in the captured continuations. You can process the samples to extract detailed profiling statistics.
You can then analyze the samples by rerunning gxprof
without arguments.
Example
Here is an example:
$ gxc -O json-benchmark.ss
$ gxprof :std/text/json/json-benchmark bio ~/Downloads/large-file.json
(time (std/text/json/json-benchmark#call-with-buffered-reader _file126_ std/text/json/api#read-json))
0.595805 secs real time
0.582223 secs cpu time (0.498561 user, 0.083662 system)
18 collections accounting for 0.239713 secs real time (0.238992 user, 0.000703 system)
1413985192 bytes allocated
82594 minor faults
no major faults
1555767072 cpu cycles
(time (std/text/json/json-benchmark#call-with-buffered-writer _tmp130_ (lambda (_g131133_) (std/text/json/api#write-json__% _object128_ _g131133_))))
0.246521 secs real time
0.246516 secs cpu time (0.234621 user, 0.011895 system)
no collections
194321304 bytes allocated
2964 minor faults
no major faults
643710478 cpu cycles
$ gxprof
... analyzing gxprof.out
Top Continuation Samples
------------------------
std/text/json/input#read-json-hash/buffer: 61 [36.970%]
std/text/json/output#write-json-string/buffer: 42 [25.455%]
std/text/json/input#read-json-hash-key/buffer: 17 [10.303%]
std/text/json/input#read-json-string/buffer: 15 [9.091%]
std/text/json/output#write-json-alist/buffer: 7 [4.242%]
std/sort#list-merge-sort: 6 [3.636%]
std/io/file#input-file-io::read: 3 [1.818%]
##table-foldl: 2 [1.212%]
std/text/json/output#write-json-list/buffer: 2 [1.212%]
std/io/file#output-file-io::write: 2 [1.212%]
##list->string: 2 [1.212%]
std/text/json/json-benchmark#benchmark-stdio-bio: 1 [0.606%]
std/text/json/input#read-json-list-next/buffer: 1 [0.606%]
std/text/json/input#read-json-object/buffer: 1 [0.606%]
std/text/json/output#write-json-hash/buffer: 1 [0.606%]
##string->number-slow-path: 1 [0.606%]
std/text/json/input#read-json-number/buffer: 1 [0.606%]
Call Graph Samples
------------------
##dynamic-wind: 392 [237.576%]
##dynamic-env-bind: 392 [237.576%]
std/text/json/input#read-json-hash/buffer: 349 [211.515%]
std/text/json/output#write-json-alist/buffer: 195 [118.182%]
##kernel-handlers: 165 [100.000%]
std/text/json/json-benchmark#benchmark-stdio-bio: 165 [100.000%]
##exec-stats: 164 [99.394%]
##time-thunk: 164 [99.394%]
std/text/json/input#read-json-list/buffer: 114 [69.091%]
std/text/json/input#read-json-list-next/buffer: 114 [69.091%]
std/text/json/json-benchmark#call-with-buffered-reader: 102 [61.818%]
std/sort#list-merge-sort: 89 [53.939%]
std/text/json/output#write-json-list/buffer: 74 [44.848%]
std/text/json/output#write-json-string/buffer: 67 [40.606%]
std/text/json/input#read-json-hash-keybuffer: 23 [13.939%]
std/text/json/input#read-json-string/buffer: 18 [10.909%]
std/text/json/output#write-json-hash/buffer: 9 [5.455%]
std/io/bio/util#&BufferedReader-read-char: 3 [1.818%]
std/io/file#input-file-io::read: 3 [1.818%]
##table-foldl: 2 [1.212%]
std/io/bio/output#bio-write-u8: 2 [1.212%]
std/io/file#output-file-io::write: 2 [1.212%]
##list->string: 2 [1.212%]
##string->number-slow-path: 2 [1.212%]
std/text/json/input#read-json-number/buffer: 2 [1.212%]
std/io/bio/util#&BufferedWriter-write-string__%: 1 [0.606%]
std/text/json/input#read-json-object/buffer: 1 [0.606%]
vyzo@dellicious:~/gerbil/src/std/text/json$
If you want to hide kernel continuations from the sample (they are not
usually very intesting), you can do so with the -k
analysis options:
$ gxprof -k
... analyzing gxprof.out
Top Continuation Samples
------------------------
std/text/json/input#read-json-hash/buffer: 61 [36.970%]
std/text/json/output#write-json-string/buffer: 42 [25.455%]
std/text/json/input#read-json-hash-keybuffer: 17 [10.303%]
std/text/json/input#read-json-string/buffer: 15 [9.091%]
std/text/json/output#write-json-alist/buffer: 7 [4.242%]
std/sort#list-merge-sort: 6 [3.636%]
std/io/file#input-file-io::read: 3 [1.818%]
##list->string: 2 [1.212%]
##table-foldl: 2 [1.212%]
std/text/json/output#write-json-list/buffer: 2 [1.212%]
std/io/file#output-file-io::write: 2 [1.212%]
std/text/json/input#read-json-object/buffer: 1 [0.606%]
std/text/json/input#read-json-list-next/buffer: 1 [0.606%]
std/text/json/input#read-json-number/buffer: 1 [0.606%]
##string->number-slow-path: 1 [0.606%]
std/text/json/output#write-json-hash/buffer: 1 [0.606%]
std/text/json/json-benchmark#benchmark-stdio-bio: 1 [0.606%]
Call Graph Samples
------------------
std/text/json/input#read-json-hash/buffer: 349 [211.515%]
std/text/json/output#write-json-alist/buffer: 195 [118.182%]
std/text/json/json-benchmark#benchmark-stdio-bio: 165 [100.000%]
##exec-stats: 164 [99.394%]
##time-thunk: 164 [99.394%]
std/text/json/input#read-json-list-next/buffer: 114 [69.091%]
std/text/json/input#read-json-list/buffer: 114 [69.091%]
std/text/json/json-benchmark#call-with-buffered-reader: 102 [61.818%]
std/sort#list-merge-sort: 89 [53.939%]
std/text/json/output#write-json-list/buffer: 74 [44.848%]
std/text/json/output#write-json-string/buffer: 67 [40.606%]
std/text/json/input#read-json-hash-keybuffer: 23 [13.939%]
std/text/json/input#read-json-string/buffer: 18 [10.909%]
std/text/json/output#write-json-hash/buffer: 9 [5.455%]
std/io/file#input-file-io::read: 3 [1.818%]
std/io/bio/util#&BufferedReader-read-char: 3 [1.818%]
##list->string: 2 [1.212%]
std/text/json/input#read-json-number/buffer: 2 [1.212%]
##table-foldl: 2 [1.212%]
std/io/bio/output#bio-write-u8: 2 [1.212%]
std/io/file#output-file-io::write: 2 [1.212%]
##string->number-slow-path: 2 [1.212%]
std/text/json/input#read-json-object/buffer: 1 [0.606%]
std/io/bio/util#&BufferedWriter-write-string__%: 1 [0.606%]
Also note, that in the call graph samples, some procedures may have more than 100% coverage. This is not a bug; a procedure may appear multiple times within the same continuation sample because of a recursive call.