1. 先说结论:这不是 C++ 和 .NET 在打架,是两种调用模型在打架
前几天调一个图像预处理接口,里面有一行数组原地反转,C++ 那边用了 std::reverse,.NET 那段用了 Array.Reverse。我顺手单独拉出来跑了组 benchmark,结果比预想的有意思:小数组区间 C++ 几乎是碾压,大数组区间 .NET 反过来把 C++ 按住了。
这里说的"原地反转",指的是不创建新数组,直接把 int32 数组里的元素头尾对调。测试前每组先预热 50 轮,正式跑 100 轮取中位数,避免温度、GC、JIT 冷启动带来的噪声。结果长这样:
| 数组长度 | C++(std::reverse) | C#(Array.Reverse) | 胜出 |
|---|---|---|---|
| 16 | 2.3 ns | 21.5 ns | C++ |
| 64 | 4.8 ns | 31.2 ns | C++ |
| 256 | 18.6 ns | 58.4 ns | C++ |
| 1,024 | 93.2 ns | 139.8 ns | C++ |
| 4,096 | 331 ns | 305 ns | .NET |
| 1,000,000 | 0.62 ms | 0.44 ms | .NET |
| 10,000,000 | 7.3 ms | 5.6 ms | .NET |
所以"反杀"不是标题党。但它跟很多人印象里的"C++ 天生比 .NET 快"关系不大,真正原因是两条技术路线的调用成本结构完全不同。
小数组 C++ 赢在调用成本趋近于零,编译器把所有动态检查都在编译期做完了;大数组 .NET 赢在运行时为原始类型数组提供了一条非常高效的 native 快速路径,这个路径在 C++ 标准库里并没有等价物。语言本身没有高低,选型要看你热路径上的数组粒度。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 测试方案:怎么测出一份不骗人的数据
2.1 实验环境
做这种语言对比,最怕的是用 Debug 模式跑 .NET,用 Release 加满优化跑 C++,最后测出一个看起来很大、其实是编译选项造成的假差距。我这轮尽量把两边拉到接近真实工程的状态:
| 项目 | 配置 |
|---|---|
| CPU | Intel Core i7-12700K,DDR4-3600,锁定性能核 |
| 操作系统 | Windows 11 23H2(x64) |
| C++ | MSVC v143,/O2 /std:c++17 |
| .NET | .NET 8.0.2,Release x64,关闭 ServerGC |
| 计时 | std::chrono::steady_clock / System.Diagnostics.Stopwatch |
C++ 没有开 PGO,也没有开 /LTCG,因为大多数项目不会专门给这种小函数做全程序优化;.NET 也没有用 ReadyToRun 或 NativeAOT
