Contract Profiling
contract-profile
contract-profile-thunk
6.6

Contract Profiling

This package provides support for profiling the execution of Contracts.

Contracts are a great mechanism for enforcing invariants and producing good error messages, but they introduce run-time checking which may impose significant posts. The goal of the contract profiler is to identify where these costs are, and provide information to help control them.

The simplest way to use this tool is to use the raco contract-profile command, which takes a file name as argument, and runs the contract profiler on the main submodule of that file (if it exists), or on the module itself (if there is no main submodule). The tool’s output is decribed below.

 (require contract-profile) package: contract-profile

In addition to using raco contract-profile, it is possible to invoke the contract profiler programmatically. This allows for profiling particular portions of programs, and for controlling the output.

syntax

(contract-profile option ... body ...)

 
option = #:module-graph-file module-graph-file
  | #:boundary-view-file boundary-view-file
  | #:boundary-view-key-file boundary-view-key-file
Produces a report of the performance costs related to contract checking in body on standard output.

Specifically, displays the proportion of body’s running time that was spent checking contracts and breaks that time down by contract, and then breaks down the cost of each contract between the different contracted values that use it.

Additional visualizations are available on-demand, controlled by keyword arguments which specify their destination files. An argument of #f (the default) disables that visualization.

procedure

(contract-profile-thunk 
  thunk 
  [#:module-graph-file module-graph-file 
  #:boundary-view-file boundary-view-file 
  #:boundary-view-key-file boundary-view-key-file]) 
  any
  thunk : (-> any)
  module-graph-file : (or/c path-string #f) = #f
  boundary-view-file : (or/c path-string #f) = #f
  boundary-view-key-file : (or/c path-string #f) = #f
Like contract-profile, but as a function which takes a thunk to profile as argument.

Examples:
> (define/contract (sum* numbers)
    (-> (listof integer?) integer?)
    (for/fold ([total 0])
              ([n (in-list numbers)])
      (+ total n)))
> (contract-profile (sum* (range (expt 10 6))))

Running time is 24.52% contracts

319/1299 ms

(-> (listof integer?) integer?)                                  318.5 ms

#<blame>:1:0                                                    

    sum*                                                         318.5 ms

499999500000

The example shows that a large proportion of the call to sum* with a list of 1 million integers is spent validating the input list.

Note that the contract profiler is unlikely to detect fast-running contracts that trigger other, slower contract checks. In the following example, there is a higher chance that the profiler samples a (listof integer?) contract than the underlying (vectorof list?) contract.

Examples:
> (define/contract (vector-max* vec-of-numbers)
    (-> (vectorof list?) integer?)
    (for/fold ([total 0])
              ([numbers (in-vector vec-of-numbers)])
      (+ total (sum* numbers))))
> (contract-profile (vector-max* (make-vector 10 (range (expt 10 6)))))

Running time is 108.5% contracts

4539/4183 ms

(-> (vectorof (listof any/c)) integer?)                          2766 ms

#<blame>:3:0                                                    

    vector-max*                                                  2766 ms

(-> (listof integer?) integer?)                                  1772.5 ms

#<blame>:1:0                                                    

    sum*                                                         1772.5 ms

4999995000000