TypeScript Report — HW3 Language Comparison
Language / compiler / runtime version
TypeScript Version 7.0.2 (tsc --version) compiling to Node.js v22.18.0
(node --version), both confirmed inside WSL2 Ubuntu 26.04 "Resolute
Raccoon". Compilation used tsconfig.json with "target": "ES2020"
(needed for the bigint type returned by process.hrtime.bigint()),
"module": "CommonJS", and "types": ["node"] (Node's ambient type
declarations, installed as the sole dev dependency via npm install --save-dev @types/node). No other optimization flags — the emitted
quicksort.js runs on the stock V8 engine embedded in Node 22, JIT-compiled
at runtime just like plain JavaScript.
Machine: AMD Ryzen 7 5800H (8 cores / 16 threads, ~3.2 GHz base), 32 GB RAM, Windows 11 Pro host, benchmark executed inside WSL2 Ubuntu.
Random pivot / RNG
Randomized pivot selection follows CLRS RANDOMIZED-PARTITION: at every
partition call on subarray arr[p..r], a pivot index is drawn uniformly at
random from [p, r] via a helper randomInt(lo, hi) built on
Math.random() (lo + Math.floor(Math.random() * (hi - lo + 1))). That
element is swapped into position r, and a standard Lomuto partition then
runs around it (elements <= pivotValue are swapped into the growing
left region as the scan index j advances, and the pivot is finally
swapped into its correct place at i + 1). randomizedQuicksortHelper
then recurses on arr[p..q-1] and arr[q+1..r]. The same Math.random()
call (Math.floor(Math.random() * 1_000_000)) is used to generate the
input arrays for each iteration; data generation is not timed (only the
randomizedQuicksort(arr) call is bracketed by
process.hrtime.bigint()). Math.random() uses V8's xorshift128+-based
PRNG, seeded internally by the engine at startup — no explicit seed was
set. Array.prototype.sort was never called anywhere in the implementation.
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).
Results
Built and run via:
wsl.exe -d Ubuntu -- bash -c "cd '/mnt/c/Users/granite/Documents/DSU/Algorithms 482/3/HW3/typescript' && tsc && node quicksort.js"
(tsc with no filename argument, so it reads tsconfig.json; see the
typescript/README.md for why tsc quicksort.ts alone fails to compile.)
Loop stopped after 15 iterations because the measured time for
n=32,768,000 (4.040789s) exceeded the 4.0s cutoff (well before the
25-iteration or 200,000,000-element caps were reached). Full results (also
in ../results/typescript.csv):
| n | time_seconds |
|---|---|
| 1,000 | 0.000983 |
| 2,000 | 0.000285 |
| 4,000 | 0.000259 |
| 8,000 | 0.000563 |
| 16,000 | 0.001248 |
| 32,000 | 0.002207 |
| 64,000 | 0.005005 |
| 128,000 | 0.010448 |
| 256,000 | 0.021597 |
| 512,000 | 0.043112 |
| 1,024,000 | 0.090231 |
| 2,048,000 | 0.186070 |
| 4,096,000 | 0.382369 |
| 8,192,000 | 1.018765 |
| 16,384,000 | 1.731468 |
| 32,768,000 | 4.040789 |
Total wall-clock time for the whole run (time node quicksort.js, all 15
data-generation + sort iterations plus Node startup, not counting the
separate tsc compile step which took well under a second): ~8.8s
(real 0m8.805s).
Growth analysis
For expected O(n log n) behavior, doubling n should multiply the running
time by roughly 2 * (log2(2n) / log2(n)), which shrinks slowly from about
2.2x (near n=1,000, where log2(n)≈10) toward about 2.08x as n grows past
16 million (log2(n)≈25 at n≈32,768,000). The smallest few n values
(1,000 → 2,000 → 4,000) show erratic, even sub-1x ratios (0.29x, 0.91x)
purely because their absolute times are a fraction of a millisecond —
right at the resolution floor of process.hrtime.bigint() combined with
V8 JIT warm-up (the interpreter/baseline compiler runs the first few calls
before the optimizing compiler kicks in), so timing noise dominates any
real signal there. From n=8,000 onward the observed per-doubling ratios
settle into the 1.70x–2.66x range, clustering close to the theoretical
~2.1x–2.2x band — e.g. 64,000→128,000 at 2.09x, 512,000→1,024,000 at 2.09x,
1,024,000→2,048,000 at 2.06x, all within a few percent of prediction — with
two more visible outliers at 4,096,000→8,192,000 (2.66x) and
8,192,000→16,384,000 (1.70x), plausibly V8 garbage-collector pauses as the
array size crosses generational-GC and cache-size boundaries, which then
partially cancel out (a slow doubling tends to be followed by a fast one).
Overall this is consistent with expected O(n log n) growth for randomized
quicksort. No stack-overflow or other anomalies occurred; expected
recursion depth is O(log n) (≈25 at the largest n reached here), far under
Node's default call-stack limit.
Comparison with plain JavaScript (javascript/ sibling): as expected,
since tsc compiles TypeScript straight down to ordinary JavaScript that
then runs on the exact same Node 22 / V8 engine, the two languages are not
really two different runtimes for benchmarking purposes — TypeScript's type
annotations are erased entirely at compile time and have zero effect on the
emitted code's runtime behavior. The JavaScript sibling run
(../results/javascript.csv) shows essentially identical timings at every
matching n — e.g. 1,024,000: JS 0.0856s vs TS 0.0902s; 2,048,000: JS
0.1827s vs TS 0.1861s; 4,096,000: JS 0.3697s vs TS 0.3824s; 8,192,000: JS
0.7848s vs TS 1.0188s; 16,384,000: JS 1.7361s vs TS 1.7315s — all within
normal run-to-run noise for a JIT'd, garbage-collected runtime, with no
systematic gap in either direction. The one visible difference is where
each run happened to cross the 4.0s stopping threshold: JavaScript's
n=32,768,000 iteration finished in 3.864s (just under the cutoff) and so
it went on to attempt n=65,536,000 (13.36s, its final/stopping row),
whereas TypeScript's n=32,768,000 iteration finished in 4.041s (just over
the cutoff) and stopped one iteration earlier. That is boundary noise, not
a real language-speed difference — both language's n=32,768,000 timings
(3.864s vs 4.041s) are within about 4.6% of each other, and either run
could easily have landed on the other side of the 4.0s line on a repeat
trial. This confirms the expected result stated in the assignment protocol:
TypeScript and JavaScript should perform nearly identically here because
they compile down to the same JS runtime.