C# Report — Randomized Quicksort (HW3)
Runtime / configuration
- Language: C#, .NET SDK 10.0.400 (confirmed via
dotnet --version), target frameworknet10.0. - Build configuration: Release (
dotnet build -c Release -o bin/publish), 0 warnings / 0 errors. - Executed inside WSL2 Ubuntu (distro
Ubuntu), perHW3/DESIGN.md. The prebuilt native apphost couldn't locate the runtime in this WSL install (dotnet lives under/home/granite/.dotnet, outside the apphost's default search path), so the Release build was launched viadotnet ./bin/publish/HW3CSharp.dllinstead of the apphost executable directly — this only changes the launcher, not the JIT/optimization behavior of the Release-built managed code being timed. - CPU: AMD Ryzen 7 5800H (8 cores / 16 threads, ~3.2 GHz base), 32 GB RAM, Windows 11 Pro host, benchmark executed inside WSL2 Ubuntu (per DESIGN.md machine spec).
Random pivot / RNG
A single shared System.Random instance (Rng) is used for both:
- Generating each iteration's test array (
Rng.Next(0, 1_000_000)per element,nelements, not timed), and - Choosing the randomized pivot in
RandomizedPartition(a, p, r):Rng.Next(p, r + 1)picks an index uniformly at random over the current subarray range[p, r]; that element is swapped into positionr, and then the standard LomutoPartition(CLRSPARTITION) runs against it. This mirrors CLRS'sRANDOMIZED-PARTITIONexactly.
External code
No external code used — original implementation based on CLRS's
RANDOMIZED-QUICKSORT / RANDOMIZED-PARTITION pseudocode (Cormen, Leiserson, Rivest &
Stein, Introduction to Algorithms, Ch. 5). Array.Sort/List.Sort were not used
anywhere; the sort is implemented by hand with an in-place Lomuto partition.
Results
The loop ran for 16 iterations and stopped because the elapsed time of the n =
65,536,000 iteration (6.437 s) exceeded the 4.0 s cutoff (well before hitting the
25-iteration cap or the 200,000,000 next-n cap). Total wall-clock time for the whole
run (process start to exit, via time) was 12.63 s.
| n | time_seconds | ratio vs. previous n |
|---|---|---|
| 1,000 | 0.0003593 | — |
| 2,000 | 0.0004553 | 1.27x |
| 4,000 | 0.0011562 | 2.54x |
| 8,000 | 0.0015565 | 1.35x |
| 16,000 | 0.0030929 | 1.99x |
| 32,000 | 0.0060229 | 1.95x |
| 64,000 | 0.013372 | 2.22x |
| 128,000 | 0.0259128 | 1.94x |
| 256,000 | 0.0542506 | 2.09x |
| 512,000 | 0.1210172 | 2.23x |
| 1,024,000 | 0.167438 | 1.38x |
| 2,048,000 | 0.141526 | 0.85x (anomaly) |
| 4,096,000 | 0.3047616 | 2.15x |
| 8,192,000 | 0.6149167 | 2.02x |
| 16,384,000 | 1.2798072 | 2.08x |
| 32,768,000 | 2.7475075 | 2.15x |
| 65,536,000 | 6.4370018 | 2.34x |
Full data is also in HW3/results/csharp.csv.
Growth analysis
For n log n growth, doubling n should roughly multiply the running time by
2 * (log(2n) / log(n)), which for the n ranges here works out to roughly 2.0–2.3x
per doubling. From n = 16,000 onward the observed ratios mostly sit in that 1.9x–2.3x
band (e.g. 1.99x, 1.95x, 2.22x, 1.94x, 2.09x, 2.23x, 2.15x, 2.02x, 2.08x, 2.15x, 2.34x
at the largest sizes), which is consistent with expected O(n log n) behavior for
randomized quicksort. Two anomalies are worth calling out:
- Small-
nwarm-up (n = 1,000–8,000): the first few iterations show noisier, sub-proportional or super-proportional ratios (1.27x, then a jump to 2.54x, then 1.35x) rather than the steady ~2x trend. This is the expected effect of .NET's tiered JIT compilation and general process warm-up — the earliest calls intoRandomizedQuicksort/Partitionrun under the quick, less-optimized tier-0 JIT (or are still being JIT-compiled at all) before the runtime promotes hot methods to optimized tier-1 code, so timings for the smallestnare dominated by JIT/startup noise rather than genuine algorithmic cost, which is itself microseconds at this size. - n = 1,024,000 → 2,048,000 dip: time actually decreased slightly (0.1674s →
0.1415s, a 0.85x ratio) despite
ndoubling. This is the single clearest anomaly in the run. The most likely explanation is a combination of (a) tiered JIT promoting the sort/partition methods to fully optimized code around this point (a one-time step-function speed-up that can outweigh one doubling ofn), and (b) run-to-run GC/scheduling noise from the .NET server/workstation GC doing a collection during the slower neighboring iterations. Aside from that one dip, growth from n = 2,048,000 up through n = 65,536,000 is smooth and consistently in the expected ~2.0x–2.35x per-doubling range, matching O(n log n) as expected for CLRS randomized quicksort.