← All reports

Common Lisp (SBCL) Report — CSC 482 HW3 (Language Comparison)

Language / toolchain

Algorithm / RNG

Implements CLRS's RANDOMIZED-QUICKSORT / RANDOMIZED-PARTITION (Ch. 5) directly on a simple-array of fixnum, accessed via aref:

External code used

No external code used — original implementation based on CLRS RANDOMIZED-QUICKSORT/RANDOMIZED-PARTITION pseudocode (Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, Ch. 5). Standard library only, no Quicklisp packages.

Results

Full results (also in HW3/results/lisp.csv), 17 iterations, stopped when iteration 17 (n = 65,536,000) took 8.800s, exceeding the 4.0s cutoff:

n time_seconds
1,000 0.000000000
2,000 0.000000000
4,000 0.000000000
8,000 0.004000000
16,000 0.000000000
32,000 0.004000000
64,000 0.004000000
128,000 0.012000000
256,000 0.024000000
512,000 0.056000000
1,024,000 0.124001000
2,048,000 0.248000000
4,096,000 0.531999000
8,192,000 0.967999000
16,384,000 1.603999000
32,768,000 3.775999000
65,536,000 8.800000000

Max n reached: 65,536,000, time 8.800s (this is the iteration that triggered the stop, per the >4.0s rule). Total wall-clock time for the whole run (SBCL process start to exit, per time around the sbcl --script invocation): ~17.2s real.

Growth analysis

For O(n log n) behavior, doubling n should roughly double the runtime times a slowly growing log factor — the ratio T(2n)/T(n) should sit a bit above 2 and should trend toward 2 from above as n grows. From n = 128,000 upward (past the noise floor discussed below), the observed per-doubling ratios are 128,000→256,000: 2.00x, 256,000→512,000: 2.33x, 512,000→1,024,000: 2.21x, 1,024,000→2,048,000: 2.00x, 2,048,000→4,096,000: 2.15x, 4,096,000→8,192,000: 1.82x, 8,192,000→16,384,000: 1.66x, 16,384,000→32,768,000: 2.35x, 32,768,000→65,536,000: 2.33x. These cluster in the ~1.7–2.35x range, consistent with O(n log n) (a pure O(n) algorithm would show ratios flat at ~2.0x; O(n^2) would show ratios near 4.0x) — the data is clearly not quadratic and sits in the expected linearithmic neighborhood, though the ratios are noisier than the smoother per-doubling curves seen in the round-1 compiled languages (C/C++/Rust/Go).

Anomalies. Two are worth flagging. First, internal-time-units-per-second reports 1,000,000 (nominally microsecond resolution), but the smallest nonzero measured values are all exact multiples of 0.004 seconds (4 ms), and several early iterations (n = 1,000–4,000, and oddly n = 16,000) read exactly 0.0. This indicates the actual clock backing get-internal-real-time in this WSL2/SBCL combination has ~4 ms real granularity regardless of what units it reports in — a coarser effective resolution than the microsecond/nanosecond timers available to e.g. C's clock_gettime, Rust's Instant, or Java's System.nanoTime() in the earlier round-1 runs on this same machine. This makes the small-n rows unreliable for growth-rate analysis (hence excluding them above) but does not affect the large-n rows, where real elapsed time is many multiples of the 4 ms quantum. Second, SBCL compiles to native machine code ahead of time (there is no JIT warm-up or GC pause comparable to the JVM/.NET, and this is a single-generation-friendly workload with few large long-lived allocations), so per-iteration timings otherwise show the smooth, low-noise behavior typical of natively-compiled languages once past the clock-floor region — comparable in shape to the C/Rust/Go curves and clearly faster per element than the interpreted/managed-runtime languages, matching the expectation that a natively-compiled Lisp should perform in the same tier as C/C++/Rust/Go rather than Python, and at least as well as Java/C#.