性能

原生速度,平坦的尾部

AOT 编译到 LLVM、零分配的热路径、arena 内存、单线程事件循环,让 SuperJ 进入与已发表最快框架同一档次 — 而且延迟曲线保持平坦,因为没有回收器会停顿。

515K
请求 / 秒 — wrk 上限约 423K,瓶颈在它身上
~1.1×
p99 在 p50 的 ~1.1× 以内 — 尾部平坦
0
GC 停顿 — arena 内存,无需扫描

HTTP

世界上最快的服务器

一个跑在内置 HTTP 协议栈上的 Web 服务器,编译成单一原生二进制,在回环节点上加压测试。服务器钉在一个核上,压力发生器钉在另一个核上。它的响应速度比 wrk 发请求的速度还快。

压力发生器吞吐p50p99
wrk(1 进程)406,762 req/s156µs167µs
SuperJ WebClient(流水线 ×16)515,000 req/s
wrk 上限(同一台机器)~423,000 req/s瓶颈是 wrk,不是服务器

Ryzen 9 9950X3D · 服务器在 CPU 4,客户端在 CPU 3 · 回环 TCP · 100 条 keep-alive 连接

WebServer.sj
class HelloHandler implements HttpRequestHandler {
  private final ByteArray response;
  HelloHandler() {
    String body = "Hello from SuperJ!\n";
    this.response = new ByteArray(
      "HTTP/1.1 200 OK\r\nContent-Length: "
      + body.length() + "\r\n\r\n" + body);
  }
  public void handle(HttpRequest r, SessionWriter w) {
    this.response.rewind();  // 复用 — 零分配
    w.write(this.response);
  }
}
  • aot

    没有 JIT 预热

    通过 LLVM 编译为原生代码。从第一个请求起就是全速 — 没有 profile,没有反优化。

  • 无 gc

    无停顿

    arena 内存确定性地回收。这就是负载下延迟曲线保持平坦的原因。

  • 零分配

    热路径无分配

    响应缓冲构建一次,每次请求 rewind 复用。稳态服务零分配。

  • 单循环

    无锁开销

    一个事件循环,一个线程 — 热路径上没有锁、没有上下文切换、没有跨核缓存流量。

superj_web

边缘服务器 — 比 nginx 快 1.85×,比 Caddy 快最多 4.8×

同一套协议栈,包成一个完整的边缘 Web 服务器,带 TLS 终止、路由、静态文件缓存和反向代理。尽管多了这些开销,它在同一台机器上、在每种文件大小下、在静态服务和反向代理上都击败 nginx 和 Caddy。

静态文件服务 — superj_web vs nginx vs Caddy

工作负载superj_webnginxCaddySDK 参照
小文件(59 字节)405,229 req/s218,948 req/s70,248 req/s (128B)407,094 req/s
1 KB286,710 req/s59,974 req/s
16 KB254,602 req/s59,479 req/s
64 KB146,959 req/s56,634 req/s
大文件(10MB / 1MB)1,510 req/s (14.75 GB/s) / 15,229 req/s1,066 req/s (10.41 GB/s)10,971 req/s (1MB)

Ryzen 9 9950X3D · 服务器一核,wrk 另一核 · 回环 TCP · 3 轮平均。Caddy v2.8.4,superj_web 用 --release --enterprise 构建。nginx:1.24.0,单 worker,sendfile onaccess_log off

反向代理 — superj_web vs Caddy(前端 → 后端,隔离 CPU)

文件Caddy req/ssuperj_web req/s加速
128B34,677106,817快 3.1×
1 KB33,008106,622快 3.2×
16 KB24,45688,605快 3.6×
64 KB20,02243,242快 2.2×

100 条 keep-alive 连接 · 前端在 CPU 14,后端在 CPU 15 · 每个 upstream 128 条池化 keep-alive 连接 · 原始响应转发(不重建)。

为什么它更快。 静态热路径零分配:预构建的响应、rewind() 复用、带惰性 mtime 失效的内存文件缓存。代理热路径再加上连接池(每个 upstream 128 条 keep-alive 连接)和带读取端 prepareForReuse() 的原始响应转发 — 两条路径都没有每请求分配。单线程事件循环在一个核上处理 288K req/s(静态 128B)和 107K req/s(代理 128B);与 nginx/Caddy 的差距是算法层面的,不是线程层面的。

SEDA

跨进程协调 — 88 ns/轮

SEDA sequencer 是替代共享可变状态的全序事件流。在 M4 Max 进程内测得 88 ns/轮 — 比裸跨进程 mmap 自旋(152 ns/轮)还快,因为它从不在核间穿越,不付缓存一致性代价。

应用 — xpe_db

一个真实的数据库,不只是内核

xpe_db 是一个嵌入式文档数据库,带 HNSW 向量索引、倒排索引、哈希索引,以及 JWT/认证子系统 — 从 Java 移植到 SuperJ。这个二进制不带 JVM、不带 GC、没有 JIT 预热地运行。这些是应用层面的数字,是用户真正能感受到的。

HNSW 向量索引 — SuperJ vs C++ hnswlib

字节量化(uint8)L2 距离,M=16,maxM0=32,ef=200,k=10,dims=128,n=1000。相同种子的 RNG,相同工作负载。精确 20K 查询 nanoTime 基准。

指标SuperJC++ hnswlib结果
搜索吞吐24.8K q/s23.7K q/sSuperJ 快约 5%
插入吞吐~25K v/s25.6K v/s持平
Recall@1100%100%相同

倒排与哈希索引器 — SuperJ vs Java

基准JavaSuperJ加速
InvertedIndexer.search(1024) TotalHits37 ns6 ns快 6.2×
InvertedIndexer.search(1) TotalHits35 ns3 ns快 11.7×
InvertedIndexer.build(10 万条记录)755 ns118 ns快 6.4×
HashIndexer.search(10 万次查找)22 ns17 ns快 1.3×

SuperJ:enterprise SDK,-O3(边界检查开启,无 SIMD)。Java:JDK 17,冷 JIT。macOS arm64。

JWT / 认证 — SuperJ vs Java

操作JavaSuperJ加速
JWT.sign858 ns177 ns快 4.8×
JWT.verify801 ns181 ns快 4.4×
JWT.encryptAndSign1,238 ns364 ns快 3.4×
JWT.verifyAndDecrypt1,237 ns342 ns快 3.6×

案例研究 — superK

superK vs kdb+ — 14 项胜 13 项,最高 84×

superK 是一个用 SuperJ 从零构建的时序数据库 — 完全兼容 q 语言与 q-IPC 线协议:连接 kdb+ 的 q 客户端无需修改即可连接 superK,在 kdb+ 上运行的 q 脚本也能在 superK 上运行。相同查询语言、相同数据模型、相同客户端协议。没有 JVM、没有 JIT、没有垃圾回收器;设计上单线程,靠进程而非线程扩展。

基准说明。论文 "Benchmarking Specialized Databases for High-frequency Data"(arXiv:2301.12561)在加密货币交易所数据 — 成交与订单簿 — 上定义了 14 项查询基准,涵盖成交量聚合、VWAP、市场深度、买卖价差、NBBO、对数收益率与波动率。它旨在比较 kdb+ 与 ClickHouse、InfluxDB、TimescaleDB。kdb+ 胜出。我们用同样的 14 项基准、同样的数据形状、同样的查询 — 两台引擎在同一台机器、同一个核、同一份数据上,用同样的方式计时。paper_verify.q 在计时前逐值证明两个数据库一致;两引擎都通过各自 REPL 跑相同 q 文本,所以时钟覆盖用户命中的完整路径(解析、求值、格式化、打印),而非只是孤立内核。

ID查询superK (µs)kdb+ (µs)胜者比例
T-V1成交量,按符号7,49514,303superK1.9×
T-V2成交量,按符号 × 交易所227,712222,269kdb+1.02×
T-VWAP成交量加权均价2,0891,794superK1.2×
O-T订单簿最优档4.8402superK84×
O-B1最优买价1,1112,556superK2.3×
O-B2最优买价 & 卖价5,25614,208superK2.7×
O-S价差709827superK1.2×
O-V1订单簿成交量,按符号40,29461,012superK1.5×
O-V2订单簿成交量,按符号 × 交易所164,769513,127superK3.1×
O-NBBO全国最优买卖价420859superK2.0×
C-R对数收益率5,5715,774superK1.04×
C-VT波动率,成交2,8683,048superK1.06×
C-VO1波动率,订单簿(买)5,5225,735superK1.04×
C-VO2波动率,订单簿(买 & 卖)32,09737,053superK1.2×

AMD Ryzen 9 9950X3D · 30 GB 内存 · NVMe · 两引擎均 taskset -c 2(隔离核)、热缓存 · 10 轮迭代取均值 · 数据:30 个分区 + 1 个 NBBO 日,约 20M 成交行、约 33M 订单簿行,39 GB,3 符号 / 2 方向 / 5 交易所 · superK commit 2c84085,superj build --release(AVX2,无越界检查,-O3)· kdb+ 5.0

superK 14 项胜 13 项。唯一一负 — T-V2 的 1.02× — 在测量噪声之内;两引擎实质打平。胜幅从 1.04×(C-R)到 84×(O-T)。胜在存储与查询引擎本身,而非不同的数据形状:superK 实现了与 kdb+ 相同的 splayed、parted、p# 索引列式模型,通过 LLVM AOT 编译,热路径零分配。

账本

SuperJ vs C — 全景图

我们在同一台机器上、相同工作负载、相同迭代次数下跑了 54 项与 C 正面交锋的基准(以及相关时与 C++ simdjson/hnswlib、Rust、Java 的对比)。下面每条柱按速度比例切分:琥珀色是 SuperJ,蓝色是 C。50/50 是平局;如果 SuperJ 快 2×,它的段就长一倍。我们展示每一项基准,包括我们输的。

11 31 12 共 54 项基准 — 42 项(78%)为胜或平
琥珀色 = SuperJ · 蓝色 = C · 更长的段 = 更快 · 显示的比例为较快一方的加速倍数
算术循环
int[] 数组扫描
字符串拼接(链式)
1.63x
String valueOf (int/long)
1.25x
String charAt (ASCII)
HashMap put (Object)
HashMap get (Object)
对象分配
对象字段访问
1.25x
方法调用(递归 fib)
UDP 网络
SWAR parseInt (LTO)
1.35x
HashMap get 散布键 vs C
4.00x
HashMap get stride-256 vs C
7.17x
SHA-256 (64B, 硬件)
BitSet set 顺序 (LTO)
2.04x
BitSet get 顺序 (LTO)
1.30x
BitSet 扫描 (LTO)
1.70x
JSON 小 7B vs simdjson
1.24x
JSON 数组 22B vs simdjson
1.08x
JSON 对象 77B vs simdjson
1.13x
JSON 大 5.7KB vs simdjson
1.09x
i128 add/mul/div
i256 add/mul
i256 div(一般)
3.35x
i512 add/mul
i512 div(一般)
10.42x
i128 除以常量
i128 数组扫描
i256 数组扫描
i512 数组扫描
secp256k1 keygen
secp256k1 sign
secp256k1 verify
secp256k1 recover
regex-redux vs Rust
fannkuch-redux
1.03x
spectral-norm
1.27x
mandelbrot
reverse-complement
1.76x
k-nucleotide
1.75x
n-body
fasta
1.05x
Primes(筛+trie)
1.06x
Base64 编码
Base64 解码(无边界检查)
1.10x
Brainfuck
matmul N=64 SIMD-tile
2.57x
matmul N=128 SIMD-tile
2.02x
matmul N=512 SIMD-tile
1.15x
HNSW 搜索 vs hnswlib
HNSW 插入 vs hnswlib
距离函数 64 维
距离函数 512 维
SuperJ C / C++ / Rust 54 项基准 · 比例 = 较快一方的加速倍数

柱子是怎么算出来的。 每项基准在同一台机器上、相同工作负载、相同迭代次数下让 SuperJ 与 C(或标注处的 C++ simdjson / C++ hnswlib / Rust)对决。SuperJ 用 clang -O3 链接编译;C 用 clang -O3(C++ 基准用 g++ -O3 -mcpu=native)。3 次热运行取最优。每条柱的切分点是速度比例:如果 SuperJ 用的时间是 C 的 r×,SuperJ 的段就是 1/(1+r),C 的段是 r/(1+r) — 所以快 2× 时 SuperJ 的段是 2:1,平局是 50/50,输了 SuperJ 的段就缩短。快 5% 以上算胜,±5% 以内算平,慢 5% 以上算负。没有任何基准被挑选 — 这 54 套覆盖算术、数组扫描、字符串操作、对象分配/访问、方法分派、网络、整数解析、哈希表(Object 键、3 种键分布)、SHA-256、BitSet、JSON(4 种文档大小)、宽整数算术(i128/i256/i512)、secp256k1 ECDSA、8 个 Benchmarksgame 移植、Base64、Brainfuck、SIMD 矩阵乘法,以及一个完整的 HNSW 向量索引。完整结果表和方法论在源码树的 benchmarks/RESULTS.mddoc/hnsw_performance.md 中。

SuperJ 赢在哪里

  • 计算

    哈希表

    普通对象 put/get 与 C 持平;在对抗性键上比 tidwall C 快最多 7×;散布 int 键上快 4×。

  • 数值

    宽整数除法与 SIMD matmul

    i256/i512 一般除法比 C 快 3-10×;缓存分块的 SIMD-tile GEMM 在每种矩阵尺寸上比自动向量化的 C 快 1.15-2.57×。

  • 应用

    vs Java

    索引器和 JWT 上快 3.4-11.7× — 热路径零分配、原生加密内联函数、无 GC。

C 仍然赢在哪里

  • 差距

    字符串拼接与 valueOf

    在分配受限路径上慢 1.25-1.63× — SuperJ 分配真实的堆 String;C 写一个一次性的栈缓冲。charAt 持平。

  • 差距

    Base64 解码

    慢 1.10× — stride-3/4 模式上每元素指针重载 + 边界检查。#2376 之后编码已持平。--no-bounds-check 能补上大部分剩余差距。

  • 差距

    JSON 对象(77B)vs simdjson

    慢 1.13× — 64B 以下文档的逐字节标量尾部。SuperJ 在 7B 和 22B 文档上比 simdjson 快,5.7KB 上只差 9%。

  • 差距

    带边界检查的随机访问

    BitSet 扫描、对象字段访问、reverse-complement — 每次 access 都有 C 不带的检查。--no-bounds-check 能补上大部分。

复现

自己跑这些基准

HTTP 服务器和压力发生器随 SuperJ 发布在 demo/sj/demo/WebServer.sjdemo/sj/demo/WebClient.sj。边缘服务器的基准脚本在 superj_web 仓库中。

terminal
# HTTP 服务器 + wrk
superj compile "$SJ_HOME/demo/sj/demo/WebServer.sj" --sdk-path "$SJ_HOME/sdk" --link --output webserver
./webserver 8080 &
wrk -t2 -c100 -d30s --latency http://127.0.0.1:8080/

# 流水线客户端(当 wrk 是瓶颈时)
superj compile "$SJ_HOME/demo/sj/demo/WebClient.sj" --sdk-path "$SJ_HOME/sdk" --link --output webclient
./webclient 8080 64 16 15

# 跨核扩展 — 同一个二进制,多跑几份
for i in 1 2 3 4; do ./webserver 8080 & done