惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
小众软件
小众软件
F
Fortinet All Blogs
博客园 - 叶小钗
博客园_首页
D
DataBreaches.Net
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
博客园 - Franky
Martin Fowler
Martin Fowler
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
A
About on SuperTechFans
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
M
MIT News - Artificial intelligence
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog

博客园 - lzhdim

25个每个开发人员都应该掌握的JavaScript 数组方法 20260831 - 个人小作品更新 C#使用SQLite数据库例子 - 开源研究系列文章 20260830 - 个人小作品更新 20260829 - 个人小作品更新 13、JavaScript事件循环机制 - JavaScript学习系列文章 首款媲美分体水冷的一体水冷!微星CORELIQUID E15 360水冷散热器评测:目前为止 锐龙9 9950X3D最强性能释放 JavaScript常见的内存泄露问题 - JavaScript学习系列文章 C#开发的颜色取色器 - 开源研究系列文章 - 个人小作品 成长环境 - 我的闪存 提高 SQL 语句执行速度的方法 JavaScript安全最佳实践 关闭iPhone一些默认设置,让iPhone更流畅 20260814 - 个人小作品更新 JavaScript对象与元编程 20260812 - 个人小作品更新 JavaScript 模块化系统 Markdown 语法完全指南 C# 通过 Windows API 实现进程内存读写操作 JavaScript 函数进阶课题 C#开发的基于钩子技术监测键鼠事件的例子 - 开源项目研究文章 JavaScript异步编程的演进 近十年最帅风冷!酷冷至尊V8 ACE 3DHP散热器评测:单塔能干翻双塔 20260723 - 个人小作品更新 C# 中读取CPU、硬盘和内存温度 雷柏VT3s MAX鼠标评测:中小手型用户也没有被遗忘 顶尖性能的小号电竞鼠标来了 JavaScript 中的原型与继承 1分钟查清局域网所有IP 20260714 - 个人小作品更新 WorkBuddy 完整教程手册
C#已经不是当年的C#了——我用它写了个微秒级数据库引擎
lzhdim · 2026-06-05 · via 博客园 - lzhdim

当你看到"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日