首页/文档/性能

性能

了解 CK 如何让专注的计算高效运行。

CK 面向数值计算内核:这类紧凑、计算密集的例程通常嵌入较大的应用中。新的 v0.15.1 对比在同一台机器上,用 CK 原生、C++、Rust、Java、Node.js JavaScript 和由 Node.js 调用的 CK WebAssembly 运行十一项固定算法。此前的两项负载作为另一轮历史测量保留在下方。结果对比的是具体实现与构建方式,而非语言的整体性能排名。

v0.15.1:十一项算法、六种实现#

首页选择器按 CK 原生相对 JavaScript 的倍率排列这些算法。图表行按显示倍率从高到低排序,舍入后相同则 CK 排在前面。

首页选择器默认显示 v0.15.1 中排名第一的求和负载。十一项负载选项均显示 v0.15.1 六项结果的原始报告中的五行:CK 原生、C++、Rust、Java 和 Node.js JavaScript;下方另有可切换 baseline 和 SIMD128 profile 的 strict WASM 图。v0.15.1 原始报告仍包含由 Node.js 调用的 CK WASM;strict WASM 则单独测量 CK、Clang 和 Rust。两图下方用一条说明标出不同参考系:v0.15.1 图以 JavaScript = 1.0×,strict WASM 图以同一 profile 下较快且兼容的 Clang 或 Rust 实现 = 1.0×;耗时和倍率不可直接比较。Strict 用时并非 v0.15.2 正式版二进制测得。此前两项历史负载仍为独立选项。

v0.15.1 套件中的 CK Wasm 使用 O3 与 simd128 特性配置构建,采用严格浮点语义,并关闭溢出与边界检查。该配置允许 SIMD 指令,但具体内核仍可能保持标量。计时样本来自预热后的重复 Node.js 到 Wasm 调用,包含宿主分派,以及必要的输出或工作区初始化;不含模块实例化、内存准备、读取或复制输入、编译和输出哈希。

在 v0.15.1 图表中,首页选择器将 Node.js JavaScript 固定为 1.0×。这些图中的倍率均为相同算法与输入下,JavaScript 耗时中位数除以对应实现的耗时中位数。低于 1.0× 表示比 JavaScript 慢。原始报告记录每轮样本、构建和运行环境、输入输出哈希以及测试顺序;不同算法之间不计算“综合倍率”。

工作负载包括逐项融合算术、u32 归约求和、f64 点积、3×3 高斯图像模板、按行-内层-列顺序执行的矩阵乘法、最小最大值归一化、七次多项式求值、确定性蒙特卡洛 π 采样、四分支分段内核、输入输出共 128 MiB 的流式变换,以及稠密图 O(V²) Dijkstra 最短路径。“融合”指单次遍历中组合算术运算,不代表使用硬件融合乘加。流式变换的规模本身也不能证明已达到内存带宽上限。矩阵乘法直接执行内核,不调用 BLAS 或 NumPy。

有数组输入的工作负载中,六种实现都读取同一份预生成的小端输入;蒙特卡洛模拟则从相同的确定性种子出发,在计时内生成采样点。计时范围是一次完整内核计算,包含每次必须完成的输出或工作区初始化;编译、进程启动、读取输入、Wasm 实例化、内存扩展与输入复制,以及输出哈希都不计入。CK Wasm 在 Node.js 中的结果计入 JavaScript 到 Wasm 的调用开销。CK 原生与 CK Wasm 由同一份 .ck 源码编译;Wasm 使用 O3 SIMD128 配置。该配置允许符合条件的循环使用 SIMD,但严格浮点归约和其他循环仍可能是标量。JavaScript 使用类型化数组,Java 使用预热的 OpenJDK 21 进程;每个实现的完整输出都与独立参考结果比对。

详见 v0.15.1 完整测量数据及基准源码与精确复现命令。结果仅适用于报告中记录的 Apple M5 Max 机器、工具链版本、具体算法和输入规模。将结论用于其他程序之前,应在目标机器上测量代表性的实际负载。

Strict 同目标 WebAssembly 对比#

主要的 strict WASM 报告使用 CK、Clang 和 Rust,分别在 baseline 与 simd128 配置下测量十一项固定内核。首页每项负载面板中的 strict 图可切换这两个 profile,并展示同一 profile 内三种实现相对于较快兼容 Clang 或 Rust 路线的耗时与吞吐率;其报告身份和参考系与上方可见的五行 v0.15.1 图独立。第一次完整运行中,baseline 几何平均为 1.117×(最小值 0.920×;11 项中 0 项低于 0.90×),SIMD128 为 1.016×(最小值 0.923×;11 项中 0 项低于 0.90×)。同一 ckc 二进制的第二次完整运行中,baseline 为 1.118×(最小值 0.914×;0/11 项低于 0.90×),SIMD128 为 1.008×(最小值 0.920×;0/11 项低于 0.90×)。两次运行的两个 profile 均分别达到几何平均 0.95 和单项 0.90 的目标;样本没有合并。可下载第一次运行源码包、第二次原始报告及其源码包。源码包说明列出公开源码和复现命令。

本次测量使用开发候选,报告版本字符串为 ckc 0.15.1;它既不是正式发布的 v0.15.1 构件,也不是正式版 v0.15.2 二进制。其二进制 SHA-256 为 5db7c4db4146dda00020941f1047a1ade794741374e9446af4b2669de8979e3b,构建与测量时使用的干净源码 checkout 均为 08292f18b6374f9636bae5ab07fbfcb8e9e30091。报告注明二进制与源码的对应关系未经独立验证,因此以准确的二进制 hash 为准。第一次报告 SHA-256:ed56abd762d3d02404ae92b09ce213f8e8ef95f378020dea0f4b7355f302351e;第二次报告 SHA-256:4384dc2262a4a967a40d1b6f04096235def2b51408c12ae9c363c7946243378b。两次测量均在 Apple M5 Max、macOS 26.6.2(Darwin 25)、arm64 上完成,使用 Clang 22.1.8、Rust 1.90.0 和 Node.js 24.14.0。两份报告使用相同的 ckc 二进制和源码 hash,但分别保留自己的模块 hash:Rust baseline 与 SIMD 模块因嵌入生成目录路径而各相差 8 字节;特性/指令探测和完整输出校验一致。这次开发测量不改变 v0.15.1 正式版本的身份。此前各次报告仍可查阅:dc75e053d02e 第一次运行、dc75e053d02e 复测、dc61ebdb37ee 第一次运行和dc61ebdb37ee 复测,每份均有对应报告的源码包。旧 dc61 源码 ZIP 曾错误嵌入 dc75 报告字节;现已更正为嵌入与清单匹配的 dc61 JSON。dc75 首次运行的原源码 ZIP 字节保持不变。历史报告 JSON 与经 hash 校验的基准源码均未修改;这些复测报告此前未包含在官网构建产物中。

两个 profile 都使用 O3、相同的确定性输入、维度、算法与迭代顺序。baseline 选择 MVP、MULTI_VALUE 与 BULK_MEMORY,不启用 SIMD;simd128 在此基础上增加 SIMD128,且禁用 Relaxed SIMD。所有实现都使用 strict 浮点语义:不启用 fast math、重结合或 FMA 收缩。有限 f64 输出逐位比较,包括正负零和次正规数;NaN 按类别比较,u32 输出精确比较。初始化可复用工作区时,宿主会检查输入、输出和工作区范围两两不重叠,之后复用这些固定区域进行调用。这满足 CK noalias、C++ __restrict 和 Rust unsafe 区域不重叠的前置条件。WASM ABI 中的内存边界和整数溢出仍明确设为 unchecked。

报告按已选择的能力 profile 比较实现,同时保留实际模块要求和声明差异。对六个 hash 对应的模块进行受限验证后发现:CK 与 Clang baseline 可在 MVP 下验证;Rust baseline 需要 MVP 加 bulk memory;CK 与 Clang SIMD 需要 MVP 加 SIMD128;Rust SIMD 需要 MVP、bulk memory 与 SIMD128。Rust 两个模块均包含真实的 memory.fill 指令。外部声明仍有差异:Clang 声明 bulk-memory-opt;Rust 声明 bulk-memory-opt 与 mutable-globals。未观察到独立 mutable-global 用法、multi-value 指令或 Relaxed SIMD 用法。这些结果不代表各实现的声明完全相同。

热计时包含必要的输出/工作区初始化、重复循环和语言调用开销(包括 Node 到 Wasm 调用);不包含编译、进程启动、fixture 读写/复制、Wasm 实例化和内存增长、输出哈希或报告生成。冷成本表记录构件大小、每个 profile worker 的一次模块编译观测,以及 11 项负载各一次实例化和首次调用观测的中位数;这些数据不含进程或浏览器启动。单项冷首次调用显示出明显差距:baseline matmul CK 为 43.954 ms、Clang 为 13.376 ms;Dijkstra CK 为 26.896 ms、Rust 为 4.458 ms。SIMD128 matmul CK 为 20.578 ms、Clang 为 8.850 ms;Dijkstra CK 为 27.117 ms、Clang 为 5.595 ms。CK 模块大小为 baseline 28,325 字节、SIMD128 34,941 字节;Clang 分别为 4,683 和 4,821 字节。这些是冷启动观测和构件大小,不计入热运行几何平均或单项验收倍率。原始报告保留全部样本、构建命令、工具链身份与冷阶段观测。

历史同机跨运行环境对比(两项负载)#

首页保留了此前的 crosslang-benchmark-m5-max.json 报告。该报告于 2026-09-26 在 Apple M5 Max 上测量,早于 v0.15.1 套件,也没有 CK Wasm 数据。图表仍可下载原始报告,保留原始样本和报告身份。

首页图表为每项工作负载选取图中各实现的调优版:矩阵乘法有五种实现,图像模糊有六种。图中倍率标签为 JavaScript 调优版中位数除以所选调优版中位数,并作了显示舍入;因此 JavaScript 为 1.0×,柱状数据按速度从快到慢排序。柱长使用对数视觉刻度,以便容纳较大的性能差异,不能按线性比例解读。这份历史跨运行环境报告包含采用不同调优方法的 Native 实现与宿主运行时,不是统一优化级别的对照,也不能证明 Wasm 已追平。下方两张表列出图中实现的常规版和调优版数据。

每个数字是完整计算一次的运行时间中位数,单位为毫秒;越低越快。“常规”和“调优”指本测试中对应的具体实现,并不代表某种语言普遍的优化等级。

256 × 256 矩阵乘法#

实现 常规(毫秒) 调优(毫秒)
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

1024 × 1024 高斯卷积(3 × 3)#

实现 常规(毫秒) 调优(毫秒)
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

这两项计算呈现了不同的性能特点。矩阵乘法中,CK、C++ 和 Rust 调优版约需 1.87–1.89 毫秒,Java 为 3.116 毫秒,JavaScript 为 10.088 毫秒。图像卷积中,调优 C++ 和 Rust 约需 0.53–0.55 毫秒,CK 为 0.597 毫秒,Java 为 1.042 毫秒,JavaScript 为 2.203 毫秒。NumPy 调优向量表达式用时 3.444 毫秒,略长于常规版的 3.307 毫秒。结果会随算法形态和具体实现变化,不代表对语言整体性能的排名。

测试条件#

测试机器为 Apple M5 Max,运行 Darwin 25.6.0,架构为 arm64。CK 编译器版本记录在原始测量报告中;其他工具链版本为 Apple Clang 21.0.0、Rust 1.90.0、Node.js 24.14.0、OpenJDK 21.0.8、Python 3.12.14 和 NumPy 2.3.5。两项工作负载分别是 256 × 256 的 f64 矩阵乘法,以及对 1024 × 1024 的 f64 图像执行 3 × 3 高斯滤波。原始报告仍保留 NumPy 矩阵乘法测量,但它调用 Apple Accelerate 优化库,因此不列入展示的矩阵对比。图像卷积的 NumPy 常规版使用会创建中间数组的向量表达式;调优版重复使用预分配的临时数组。C++、Rust 和 CK 使用 O3 构建;常规实现面向可移植 CPU 基线,调优实现启用本机 CPU 特性并采用连续访问矩阵行的循环顺序。JavaScript 和 Java 使用持续运行并预热的进程;Java 使用 OpenJDK 21。

每个实现都按交错顺序进行七轮测量,表中给出中位数。编译、进程启动、输入准备、初始化和输出哈希计算均不计入运行时间。JavaScript、Java 和 NumPy 的持续运行进程会将语言层面的重复循环与每次函数分派计入计时。计时前,会将每个实现的完整输出缓冲区与独立参考结果进行 SHA-256 对比;两项工作负载中的十二种原始实现均与参考结果匹配;展示的矩阵对比包含十个常规版和调优版结果,卷积包含十二个。矩阵乘法参考值:a70e525b589edae101181e5ad5f5acc36c8ce2e77fd54a7222a15faf9b4a62ac。卷积参考值:7744d264c90a3103680087a715d70ab36a8effc8e9a93e58ec0f0f85dbb4c71a。

详见 M5 Max 原始测量报告、可复现的基准测试方法,或查看首页性能图表。

结果取决于具体算法、编译器与运行时版本、机器和输入规模。图中矩阵实现均为直接编写的计算内核;NumPy/Accelerate 的结果只在原始报告中留作审计。应把这些数字视为对这组测试的可复现实测,而不是对其他应用的性能预测。做出性能判断前,应在目标机器上测量自己的代表性工作负载。

另一组在 AMD EPYC 上进行的历史 CI 测量报告(2026 年 9 月)仍可查阅。它采用不同的计算任务和机器,不应与上面的图表数值合并比较。

CK 为什么能高效运行#

编译为原生机器码#

ckc run 和 ckc build 会将 .ck 程序编译为适用于目标架构的原生机器码。常规构建使用可移植的 CPU 基线;针对特定 CPU 的变体需要主动选择。程序执行计算时不需要 CK 解释器。两条命令默认生成 O3 优化构建;O3 是编译器设置,不代表速度倍数。

按明确条件进行优化#

对于合适的循环,CK 可以使用 SIMD(单指令多数据)一次处理多个互不依赖的值,也可以减少重复工作。只有满足正确性条件且目标处理器支持时,编译器才会应用这些变换。存在数据依赖或不符合条件的循环会保留常规执行路径。

根据真实工作负载调优#

对于已经完成、且有代表性工作负载的程序,PGO 可以利用观察到的运行模式指导代码生成。它会增加构建步骤;只有测试负载接近程序的实际用法时才有帮助。先使用常规构建,再根据测量结果决定是否启用 PGO 或 CPU 变体。

继续学习#

链接到仓库的完整参考文档以 main 分支为准,可能包含尚未进入最新下载版本的功能。

↵ 打开 · esc 关闭