Home/Documentation/Performance

Performance

How CK turns focused calculations into efficient code.

CK targets numerical kernels: compact, compute-heavy routines embedded in larger applications. The comparison below runs the same two calculations in CK, C++, Rust, JavaScript on Node.js, Java, and Python with NumPy, on one machine. It compares these implementations and build choices; it is not a general ranking of languages.

Same-machine comparison#

For each workload, the home page selects the tuned variant for all six languages. Each displayed multiplier is the JavaScript tuned median divided by the selected tuned median, rounded for display; JavaScript is therefore 1.0×, and the bars are sorted fastest first. Bar lengths use a logarithmic visual scale to keep large differences readable, so do not read their lengths as linear ratios. The implementations use different tuning techniques, so the chart is not a comparison at one identical optimization level. The complete paired results for all twelve variants and both workloads are below.

Each cell is the median time for one complete calculation in milliseconds; lower is faster. “Ordinary” and “tuned” name the specific checked-in variants in this suite. They do not describe universal levels of language optimization.

256 × 256 matrix multiplication#

Implementation Ordinary (ms) Tuned (ms)
CK 9.315268 1.869669
C++ 9.329641 1.866093
Rust 9.685109 1.893518
JavaScript (Node.js) 11.875417 10.087641
Java (OpenJDK 21) 9.734412 3.116292
NumPy (Apple Accelerate) 0.094030 0.086155

1024 × 1024 Gaussian convolution (3 × 3)#

Implementation Ordinary (ms) Tuned (ms)
CK 1.032351 0.597181
C++ 0.533230 0.532288
Rust 0.552982 0.547729
JavaScript (Node.js) 7.873651 2.203127
Java (OpenJDK 21) 1.595859 1.041701
NumPy 3.306759 3.443799

These kernels expose different performance characteristics. For matrix multiplication, tuned NumPy with Apple Accelerate is fastest at 0.086 ms; tuned CK, C++, and Rust take about 1.87–1.89 ms, Java 3.116 ms, and JavaScript 10.088 ms. NumPy's @ operation calls the native Accelerate matrix library, so this is not a comparison of Python loop speed. For image convolution, tuned C++ and Rust take about 0.53–0.55 ms, tuned CK 0.597 ms, Java 1.042 ms, and JavaScript 2.203 ms. NumPy's tuned vector-expression version takes 3.444 ms, slightly longer than its ordinary version at 3.307 ms. These results show how algorithm shape and libraries change the outcome; they do not rank languages in general.

What was measured#

The host was an Apple M5 Max running macOS 25.6 on arm64. Toolchains were the CK compiler identified in the raw measurement report, Apple Clang 21.0.0, Rust 1.90.0, Node.js 24.14.0, OpenJDK 21.0.8, Python 3.12.14, and NumPy 2.3.5. The workloads were a 256 × 256 f64 matrix product and a 1024 × 1024 f64 image processed with a 3 × 3 Gaussian filter. NumPy's matrix multiplication uses Apple's Accelerate backend: the ordinary case uses A @ B and copies the result to the supplied output; the tuned case uses np.matmul(..., out=...). For convolution, the ordinary NumPy case uses vector expressions that create intermediate arrays; the tuned case reuses preallocated scratch arrays. C++, Rust, and CK used O3 builds; their ordinary variants target a portable CPU baseline, while tuned variants enable native CPU features and use a row-contiguous matrix loop. JavaScript and Java run in persistent, warmed-up workers; Java uses OpenJDK 21. For the NumPy matrix library call, environment variables requested a one-thread BLAS policy; the actual Accelerate worker count was not independently verified.

Each variant ran in seven interleaved rounds; the reported value is the median. Compilation, process startup, input setup, initialization, and output hashing were outside the timed interval. Persistent JavaScript, Java, and NumPy workers include their language-level repeat loop and per-call function dispatch inside timed batches. Before timing, the complete output buffer from every implementation was SHA-256 checked against an independent reference. All twelve variants matched the reference for each workload. Matrix multiplication reference: a70e525b589edae101181e5ad5f5acc36c8ce2e77fd54a7222a15faf9b4a62ac. Convolution reference: 7744d264c90a3103680087a715d70ab36a8effc8e9a93e58ec0f0f85dbb4c71a.

See the raw M5 Max measurements, the reproducible benchmark method, or view the chart on the home page.

The results depend on the exact algorithms, libraries, compiler and runtime versions, machine, and input sizes. In particular, the NumPy matrix result includes a call into an optimized native library, while the other matrix variants are direct kernel implementations. Treat these numbers as a reproducible comparison of this suite, not a prediction for another application. Run your own representative workload on its target machine before drawing a performance conclusion.

For a separate historical measurement on AMD EPYC, see the archived CI report from September 2026. It used different workloads and a different machine, so its numbers should not be combined with the chart above.

Why CK can run efficiently#

Native machine code#

ckc run and ckc build compile a .ck program into native machine code for the target architecture. The standard build uses a portable CPU baseline; CPU-specific variants require an explicit option. The program runs without a CK interpreter in its calculation path. Optimized O3 builds are the default for these commands; O3 is a compiler setting, not a speed multiplier.

Optimizations with defined conditions#

On suitable loops, CK may process independent values together using SIMD (single instruction, multiple data), or reduce repeated work. It applies a transformation only when its correctness conditions and the target processor permit it. Loops with dependencies or unsupported shapes keep their ordinary execution path.

Optional tuning for a real workload#

For a completed program with a representative workload, PGO can use observed execution patterns to guide code generation. It adds build steps and is useful only when the measured workload resembles real use. Start with the standard build, and use PGO or CPU variants only when measurements show they help your application.

Keep learning#

Repository reference links follow the main branch and may describe features newer than the latest downloadable release.

↵ open · esc close