管理

当你看到"CRUD MVCC事务:1.2微秒"这个指标,你第一反应是哪种语言写的?C?Rust?

答案是 C#

这个结果来自一个叫 Typhon 的项目——用 .NET/C# 编写的嵌入式 ACID 数据库引擎,作者是法国工程师 Loïc Baumann,有30年实时3D引擎和系统软件开发经验。他的目标很直接:在 .NET 生态里实现 1-2 微秒级事务提交延迟。

"大家都会告诉你,做高性能数据库引擎只能用 C、C++ 或 Rust。" Baumann 在博客里写道,"我选了 C#。"

现代 C# 已经不是你认识的那个 C# 了。

GC暂停问题:固定内存就解决了

GC 会产生 10ms 级别的暂停。对微秒级延迟来说,10ms 是 10000 倍的延迟预算,直接报废。

但 Typhon 的解决方案出奇地简单:用 GCHandle.Alloc(Pinned) 把页面缓存的内存固定死。GC 永远不会碰这块内存——不会扫描,不会移动,不会压缩。它就像 malloc 分配出来的原始字节一样,只是地址固定。

关键在于:只在真正重要的内存上固定,而不是把整个堆都 pin 住。页面缓存是 Typhon 的高频路径,事务操作全部落在这块固定内存上,GC 的影响因此被压制到最小。

内存布局:unsafe可以精确到字节

托管堆会压缩和移动对象。你无法保证 B+Tree 节点恰好对齐在缓存行边界上,无法保证页面缓存在事务执行到一半时不会被 GC 悄悄移走。

现代 C# 的解法:

// 控制每个字段的偏移量、填充、缓存行对齐
[StructLayout(Explicit)]
 
// 创建固定大小的内联数组
fixed byte[128] _buffer;
 
// 裸指针和指针运算
unsafe { byte* ptr = &data; }

Typhon 的 EntityRef 结构体是 96 字节的 ref struct,完全在栈上生存,永不逃逸到堆。零 GC 压力,零间接寻址。

SIMD指令:直接调CPU指令

这是最让人震惊的部分。

System.Runtime.Intrinsics 提供 Sse42.X64.Crc32、Vector256——直接对应 CPU 指令集中的具体指令。你写的是 C# 代码,JIT 编译出来的是一条 x86 crc32 指令。

// Typhon 的 WAL 校验和计算
private static uint ComputeSse42X64(
    uint crc, ReadOnlySpan<byte> data) {
    ulong crc64 = crc;
    ref byte ptr = ref MemoryMarshal.GetReference(data);
    // 直接调用 CPU 的 crc32 指令
    crc64 = Sse42.X64.Crc32(crc64,
        Unsafe.ReadUnaligned<ulong>(ref ptr));
    return (uint)crc64;
}

每 8KB 页面只需约 1.3 微秒。运行时自动检测 CPU 是否支持 SSE4.2,不支持则自动 fallback。JIT 会把不支持的分支识别为死代码,在最终编译时完全消除。

零拷贝:直接指向内存页

EntityRef.Read<T>() 返回 ref readonly T,直接指向 pinned 内存页中的确切位置。

整个链路:方法调用 → slot 查找 → chunk ID → 页面缓存 → 指针算术 → 引用。没有拷贝,没有分配,零 GC 参与。

关键是 where T : unmanaged 约束——JIT 知道这个类型的内存布局是确定的,生成的就是指针算术,没有任何额外的运行时检查。

真正的瓶颈:内存访问模式,不是语言

来看一个数据。Ryzen 7950X 上,L1 缓存命中需要 1.4 纳秒。DRAM 访问(缓存未命中)需要 61-73 纳秒。差距 50:1

也就是说,无论你用什么语言,只要你制造了缓存未命中,CPU 就要等上 250 个时钟周期。没有任何"零成本抽象"能弥补这个差距。

Typhon 的 B+Tree 节点被设计为 128 字节——恰好是两条缓存行的大小。在 Zen4 架构上,步幅预取器会自动预取第二条缓存行。这个设计让插入延迟降低了 53%,查询延迟降低了 30%

选什么语言不重要,数据结构对缓存是否友好才重要。

一句话总结

性能的关键不是选 C、Rust 还是 C#,而是你的数据结构对缓存是否友好。现代 C# 完全有能力进入微秒级俱乐部——前提是你愿意去了解它的底层能力。

unsafe、ref struct、Constrained generics、System.Runtime.Intrinsics——这不是奇怪的边缘特性,这是 .NET 团队花了十年时间构建的系统编程基础设施。

真正值得投资的,是加深对内存层次和缓存局部性的理解。语言只是工具。


引用来源:Why I'm Building a Database Engine in C# by Loïc Baumann,2026年3月28日

 

Copyright © 2000-2022 Lzhdim Technology Software All Rights Reserved