A few things I have learned about building binaries with Chicken Scheme
The binaries
The binaries generated by the compiler have a hidden set of parameters that start with -:
. They can be used to enable debugging,
perform statistical profiling or control the garbage collector’s behavior:
$ csc gen.scm
$ ./gen -:?
[Runtime options]
-:? display this text
-:c always treat stdin as console
-:d enable debug output
-:D enable more debug output
-:g show GC information
-:o disable stack overflow checks
-:hiSIZE set initial heap size
-:hmSIZE set maximal heap size
-:hgPERCENTAGE set heap growth percentage
-:hsPERCENTAGE set heap shrink percentage
-:hSIZE set fixed heap size
-:r write trace output to stderr
-:p collect statistical profile and write to file at exit
-:PFREQUENCY like -:p, specifying sampling frequency in us (default: 10000)
-:sSIZE set nursery (stack) size
-:tSIZE set symbol-table size
-:fSIZE set maximal number of pending finalizers
-:x deliver uncaught exceptions of other threads to primordial one
-:b enter REPL on error
-:B sound bell on major GC
-:G force GUI mode
-:aSIZE set trace-buffer/call-chain size
-:ASIZE set fixed temporary stack size
-:H dump heap state on exit
-:S do not handle segfaults or other serious conditions
SIZE may have a `k' (`K'), `m' (`M') or `g' (`G') suffix, meaning size
times 1024, 1048576, and 1073741824, respectively.
Modules
The modules are declared inside a module
. As stated in the module documentation,
import scheme
is necessary inside the module. That’s easily ovelooked.
The parameters are: (module MODULE_NAME (EXPORTED_NAME_1 EXPORTED_NAME_2 ...) BODY)
:
(module lib (exported-proc exported-const)
(import scheme)
(define (exported-proc x) (* x 2))
(define not-exported-const 128)
(define exported-const (* 2 not-exported-const))
)
In this example, exported-proc
can be used by the code that import this library, but not-exported-const
will not.
This module cannot be directly imported, you will need to emit the import file for it:
csc -library -emit-import-library lib lib.scm
It will generate a shared library as well as lib.import.scm
which will allow you to import it from another file:
$ cat test.scm
(import lib)
(display (exported-proc exported-const))
(display "\n")
$ csi -script test.scm
512
$ csc test.scm && ./test
512
Profiling
Let’s try to profile this simple file:
# test.scm
(define big-list
(let proc ((size 1000000))
(if (= size 0) '() (cons size (proc (- size 1))))))
(define (my-sum l)
(if (null? l)
0
(+ (car l) (my-sum (cdr l)))))
(define (my-product l)
(if (null? l)
0
(* (car l) (my-product (cdr l)))))
(display (my-sum big-list))
(display (my-product big-list))
There are 2 different profilers built-in chicken scheme. This page gives some very interesting insights on how they work.
Both methods will generate a file that you can parse with chicken-profile
, which is included with chicken scheme.
statistical profiler
Use -:p
to sample every 10000us or :-P NUM
to sample every NUM
us. Every NUM
, the profiler will stop the execution and write
down what is the procedure currently executed.
$ csc test.scm
$ ./test -:P100 > /dev/null
$ chicken-profile PROFILE.26626
reading `PROFILE.26626' ...
procedure calls seconds average percent
---------------------------------------------------------
test.scm:3: proc 1 0.007 0.007 49.315
test.scm:8: my-sum 1 0.005 0.005 36.986
test.scm:14: my-product 1 0.002 0.002 13.698
Instrumentation profiling
Add -profile
during compilation to instruct the compiler to add extra code for profiling. Only the top-level procedures will be instrumented, which will limit the amount of details available.
$ csc -profile test.scm
$ ./test > /dev/null
$ chicken-profile PROFILE.26036
reading `PROFILE.26036' ...
procedure calls seconds average percent
----------------------------------------------
my-sum 1000001 1.757 0.000 100.000
my-product 1000001 0.899 0.000 51.166
This result is very likely due to the fact that each recursive call will trigger the instrumentation code, which will then be included into the cost. Without the profiling code, the execution time is much lower.
If your script end quickly and you want to aggregate the data gathered over several runs, -profile-name FILE
will write the profiling information into FILE
and -accumulate-profile
will append to this file after each run.
chicken-profile
will then merge the data when printing the result.
csc -profile gen.scm -profile-name gen.profile -accumulate-profile