当前位置: 首页 > article >正文

C++ vs .NET 数组原地反转实测:小数组 C++ 碾压,大数组 .NET

C vs .NET 数组原地反转实测小数组 C 碾压大数组 .NET 反杀前几天看到一篇文章《将一个序列反序在C与C#下性能比较》链接大家可以自行搜索。作者对比了 C# 的“托管/非托管”实现和 C 的 std::reverse_copy最后得出的结论是在小数组1000 个元素下 C 远超 .NET而在大数据量下 .NET 非托管优于托管。文章的切入点挺有意思但作为老 .NET 开发者我看完代码后发现这个对比其实没有控制好变量C# 版本测试的是原地反转In-place reverse只做指针/索引交换不分配新内存而 C 版本用的是 std::reverse_copy 到一个新 vector 中包含了内存分配和数据拷贝。这俩的语义完全不对等底层的成本结构也完全不一样。拿“纯计算”去和“内存分配拷贝”比性能得出的结论很容易误导人。好奇之下我决定自己动手做个控制变量的公平测试双方都只测纯粹的原地反转并使用专业工具BenchmarkDotNet 和自写的 C 高精度基准跑一下。结果非常有意思小数组下 C 确实碾压但数据量一上来.NET 的 Array.Reverse 确实能反杀下面是完整的复现过程、代码和数据分析。为什么原文章的对比不够公平我们先快速回顾一下原文章里的代码逻辑。C# 原地反转Span Slice 写法static void Reverse(Span span) {while(span.Length 1) {T firstElement span[0];T lastElement span[^1];span[0] lastElement;span[^1] firstElement;span span[1…^1]; // Slicing in each iteration introduces noticeable overhead}}C 非原地反转分配拷贝// C11std::vector test1() {std::vector rev(NumSize); // New allocation!std::reverse_copy(vec.cbegin(), vec.cend(), rev.begin());return rev;}发现问题了吗C 每次都在 new 内存。在小数组测试中内存分配的开销成了主导在大数组测试中带宽和缓存的影响又掩盖了纯粹的反转逻辑。原作者得出的“C .NET”更多是在测“分配拷贝”的耗时而不是单纯的反转算法效率。控制变量双方纯原地反转对决为了得到准确的结论我重新制定了测试规则纯原地操作全部使用 std::reverse / Array.Reverse 或手写循环绝不分配新数组。防状态污染每轮 Benchmark 前恢复原始数据连续递增的 int 数组。防死代码消除 (DCE)对反转后的结果进行消费计算 checksum。测试规模N1,000测小规模调用的固定开销N1,000,000测大规模吞吐量。.NET 端测试代码BenchmarkDotNet为了探究极限我写了四种实现原生 Array.Reverse、Span 切片、常规下标以及 Unsafe 指针。using System;using System.Linq;using BenchmarkDotNet.Attributes;using BenchmarkDotNet.Columns;using BenchmarkDotNet.Configs;using BenchmarkDotNet.Exporters;using BenchmarkDotNet.Jobs;using BenchmarkDotNet.Order;using BenchmarkDotNet.Running;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;BenchmarkRunner.Run(new Config());public sealed class Config : ManualConfig{public Config(){AddJob(Job.ShortRun.WithWarmupCount(3).WithIterationCount(8));AddColumnProvider(DefaultColumnProviders.Instance);AddExporter(MarkdownExporter.GitHub);WithOrderer(new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest));}}[MemoryDiagnoser][RankColumn]public class ReverseBench{private int[] _original Array.Empty();private int[] _work Array.Empty();[Params(1_000, 1_000_000)] public int N; [GlobalSetup] public void Setup() { _original Enumerable.Range(0, N).ToArray(); _work new int[N]; } [IterationSetup] public void Reset() { Array.Copy(_original, _work, N); } [Benchmark(Baseline true)] public void ArrayReverse() Array.Reverse(https://github.com/kmoore6155-g/pusxk2dl/issues/35); [Benchmark] public void ManagedSpanSliceReverse() ManagedSpanSlice(https://github.com/erikafd8ryan1/vcgju3pn/issues/35); [Benchmark] public void ManagedIndexReverse() ManagedIndex(https://github.com/kmoore6155-g/pusxk2dl/issues/34); [Benchmark] public void UnsafeSpanReverse() UnsafeSpan(https://github.com/erikafd8ryan1/vcgju3pn/issues/34); private static void ManagedSpanSlice(Spanint span) { while (span.Length 1) { int first span[0]; int last span[^1]; span[0] last; span[^1] first; span span[1..^1]; } } private static void ManagedIndex(Spanint https://github.com/kmoore6155-g/pusxk2dl/issues/33) { int i 0; int j span.Length - 1; while (i j) { int tmp span[i]; span[i] span[j]; span[j] tmp; i; j--; } } private static void UnsafeSpan(Spanint span) { if (span.Length 1) return; ref int left ref MemoryMarshal.GetReference(span); ref int right ref Unsafe.Add(ref left, span.Length - 1); do { int a left; int b right; left b; right a; left ref Unsafe.Add(ref left, 1); right ref Unsafe.Subtract(ref right, 1); } while (Unsafe.IsAddressLessThan(ref left, ref right)); }}(环境配置: Ubuntu 24.04, AMD EPYC 7763, .NET 10.0.3 RyuJIT AVX2)C 端测试代码C 这边同样准备了三种实现std::reverse 标准库、手写下标和手写指针。为了对标 BenchmarkDotNet我自己写了个高精度计时器。#include#include#include#include#include#include#include#include#include#includeusing Clock std::chrono::steady_clock;// Prevent compiler from optimizing away the resultsstatic inline void do_not_optimize(const void* p) {asm volatile(“” : : “g”§ : “memory”);}static void reverse_std(std::vector v) {std::reverse(v.begin(), v.end());}static void reverse_index(std::vector v) {if (v.size() 1) return;size_t i 0;size_t j v.size() - 1;while (i j) {int t v[i];v[i] v[j];v[j] t;i;–j;}}static void reverse_pointer(std::vector v) {if (v.size() 1) return;int* left v.data();int* right v.data() v.size() - 1;while (left right) {int t *left;*left *right;*right t;left;–right;}}struct Result {std::string name;int n;int iterations;double mean_ns;double ns_per_element;};static Result bench(const std::string name, int n, int iterations, const std::functionvoid(std::vector) fn) {std::vector original(n);std::iota(original.begin(), original.end(), 0);std::vector work(n);// Warmup for (int i 0; i 3; i) { work original; fn(work); do_not_optimize(work.data()); } auto start Clock::now(); std::uint64_t checksum 0; for (int i 0; i iterations; i) { work original; fn(work); checksum static_caststd::uint64_t(work[n / 2]); do_not_optimize(work.data()); } auto end Clock::now(); do_not_optimize(checksum); double total_ns std::chrono::durationdouble, std::nano(end - start).count(); double mean_ns total_ns / iterations; return {name, n, iterations, mean_ns, mean_ns / n};}int main() {std::vector sizes {1000, 1000000};std::vectorstd::pairstd::string, std::functionvoid(std::vector) fns {{“std::reverse”, reverse_std},{“manual_index”, reverse_index},{“manual_pointer”, reverse_pointer},};std::cout impl,n,iterations,mean_ns,ns_per_element\n; for (int n : sizes) { int iterations n 1000 ? 200000 : 600; for (auto [name, fn] : fns) { auto r bench(name, n, iterations, fn); std::cout r.name , r.n , r.iterations , std::fixed std::setprecision(2) r.mean_ns , std::fixed std::setprecision(6) r.ns_per_element \n; } } return 0;}(环境配置: g 13.3.0, -O3 -marchnative -stdc20)核心对决谁才是真正的性能怪兽直接来看两边跑出来的最快成绩对比数组规模 (N) C 最快实现 耗时 (Mean) .NET 最快实现 耗时 (Mean) 谁赢了1,000 manual_pointer 150.83 ns Array.Reverse 445.60 ns C 快 2.95x1,000,000 manual_pointer 162,917 ns Array.Reverse 88,716 ns .NET 快 1.84xN1,000 时 C 与 C# 的正面对位对比N1,000,000 时 C 与 C# 的正面对位对比C 原始结果CSVimpl n iterations mean_ns ns_per_elementstd::reverse 1000 200000 152.29 0.152291manual_index 1000 200000 394.25 0.394251manual_pointer 1000 200000 150.83 0.150829std::reverse 1000000 600 199966.21 0.199966manual_index 1000000 600 426880.66 0.426881manual_pointer 1000000 600 162917.17 0.162917.NET BenchmarkDotNet 完整结果测试环境如下BenchmarkDotNet v0.14.0, Ubuntu 24.04.4 LTS (Noble Numbat) (container)AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores.NET SDK 10.0.103[Host] : .NET 10.0.3 (10.0.326.7603), X64 RyuJIT AVX2ShortRun : .NET 10.0.3 (10.0.326.7603), X64 RyuJIT AVX2JobShortRun InvocationCount1 IterationCount8LaunchCount1 UnrollFactor1 WarmupCount3Method N Mean Error StdDev Ratio RatioSD RankArrayReverse 1000 445.6 ns 52.67 ns 27.55 ns 1.00 0.08 1ManagedIndexReverse 1000 3,035.8 ns 53.50 ns 27.98 ns 6.83 0.40 2UnsafeSpanReverse 1000 4,873.0 ns 436.03 ns 193.60 ns 10.97 0.75 3ManagedSpanSliceReverse 1000 9,961.2 ns 120.77 ns 43.07 ns 22.43 1.30 4ArrayReverse 1000000 88,716.0 ns 761.39 ns 398.22 ns 1.00 0.01 1UnsafeSpanReverse 1000000 349,287.4 ns 22,874.65 ns 11,963.88 ns 3.94 0.13 2ManagedIndexReverse 1000000 412,116.6 ns 18,274.90 ns 9,558.12 ns 4.65 0.10 2ManagedSpanSliceReverse 1000000 501,977.0 ns 25,291.77 ns 13,228.09 ns 5.66 0.14 2为什么会出现这种两级反转小数组场景C 的主场在处理 1000 个元素时C 的指针版开销极小。没有边界检查极致紧凑的循环编译器直接将其优化到了硬件指令的极限。而 .NET 虽然 Array.Reverse 很快但在小数组下托管环境的方法调用开销、类型检查等固定成本占比就凸显出来了导致略逊一筹。大数组场景.NET 的反杀当数据量来到 100 万时.NET 的 Array.Reverse(int[]) 展现出了恐怖的吞吐量直接拉开了近一倍的差距。为什么因为 .NET 运行时的 Array.Reverse 针对基元类型Primitive types做了深度优化底层大概率走的是专属的 JIT 路径或高度优化的 SIMD/向量化指令。反观我们自己手写的 Unsafe 代码或者原生 C 循环如果没有显式进行向量化优化在大吞吐量面前反而打不过官方的基础库。永远不要盲目自信手写算法测试数据证实了原知乎文章里的一个现象用 Span Slice 的写法确实是最慢的切片开销大。但同时我们也发现在 .NET 中哪怕你用上了 Unsafe 指针操作依旧跑不过原生的 Array.Reverse。这告诉我们永远优先相信标准库。那帮写 Runtime 的微软大佬底层的骚操作远比我们手写的 while 循环要多得多。结语抛开场景谈性能就是耍流氓。通过控制好“原地反转”这个核心变量我们看到了 C 在微操作上的极致低开销也看到了当代 .NET 在大数据吞吐和标准库优化上的强悍实力。

相关文章:

C++ vs .NET 数组原地反转实测:小数组 C++ 碾压,大数组 .NET

C vs .NET 数组原地反转实测:小数组 C 碾压,大数组 .NET 反杀? 前几天看到一篇文章:《将一个序列反序,在C与C#下性能比较》(链接大家可以自行搜索)。作者对比了 C# 的“托管/非托管”实现和 C 的…...

专业的无代码多端协同办公哪家好

在当今数字化办公的浪潮下,无代码多端协同办公平台成为众多企业提升办公效率、降低数字化门槛的首选。然而,市场上的相关产品众多,企业该如何选择呢?今天,我们就来详细分析一下,为大家推荐一款优秀的无代码…...

Notepad--:跨平台文本编辑器的技术架构与国产化实践

Notepad--:跨平台文本编辑器的技术架构与国产化实践 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器,目标是做中国人自己的编辑器,来自中国。 项目地址: https://gitcode.com/GitHub_Trending/no/notepad-- Notepa…...

ChatTTS长文本处理实战:AI辅助开发中的性能优化与避坑指南

最近在做一个AI辅助开发的项目,其中用到了ChatTTS来做文本转语音。功能本身挺酷的,但当我尝试处理一篇几千字的长文章时,问题就来了:程序直接卡死,或者内存占用飙升,生成的语音也断断续续的。这让我意识到&…...

三线OS突破20个月!科伦博泰TROP2 ADC在肺癌红海杀出重围

肺癌,历来是全球肿瘤研发领域产出最丰富、竞争也最残酷的“兵家必争之地”。 从开启靶向时代的EGFR-TKI,到重塑治疗格局的PD-(L)1免疫疗法,再到如今势头迅猛的双抗与ADC,各类技术路线持续涌入这一数百亿美元规模的治疗市场123。创…...

《Linux 是怎样工作的》第 3 章 进程管理

本章完整拆解了 Linux 进程从创建 → 执行 → 结束的全生命周期,深入剖析fork()、execve()、_exit()的底层实现、内存模型与工程实践,是理解 Linux 多任务、调度与内存管理的核心基础。一、3.2 fork() 函数:进程的复制创建1. 核心定义与设计思…...

《Linux 是怎样工作的》第 2 章:用户模式实现的功能

一、先建立核心认知:两个世界的边界 计算机系统被严格划分为两个隔离的运行环境,这是保障系统安全与稳定的基础: 内核态(Kernel Mode):相当于「小区物业」,唯一能直接操作 CPU、内存、硬盘、网…...

全国多地设备售后如何统筹?“售后管理系统”一键打通地域壁垒

摘要:对于布局全国市场的机电企业而言,售后网点是服务落地的核心载体,但当前多数企业的全国售后网点普遍存在“各自为政”的困境——区域壁垒突出、资源互不互通、工单分配失衡,导致部分网点忙闲不均、资源浪费严重,同时影响售后响应效率与服务标准化水平,制约企业售后质…...

可视掏耳勺哪个牌子好?西圣蜂鸟可视挖耳勺实测对比,家用精准入

​如今可视挖耳勺已经成为很多家庭常备的护理工具,尤其是家里有老人和孩子的用户,对产品的清晰度、安全性、舒适度都有更高要求。西圣Find X和蜂鸟3 Plus是目前百元价位里关注度较高的两款产品,它们在设计思路和功能侧重上有所不同。这次我们…...

OpenClaw技能市场巡礼:Top10 GLM-4.7-Flash增强模块推荐

OpenClaw技能市场巡礼:Top10 GLM-4.7-Flash增强模块推荐 1. 为什么需要关注技能市场? 第一次接触OpenClaw时,我以为它只是个简单的自动化工具。直到在ClawHub技能市场看到有人用GLM-4.7-Flash模型实现了简历自动打分系统,才意识…...

如何快速修复ROG游戏本色彩异常:G-Helper完整配置恢复终极指南

如何快速修复ROG游戏本色彩异常:G-Helper完整配置恢复终极指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other models …...

GPT-4o 新手入门指南:从零开始构建你的第一个智能对话应用

GPT-4o 新手入门指南:从零开始构建你的第一个智能对话应用 作为一名刚接触大模型开发的程序员,面对 GPT-4o 这样的新工具,你是不是既兴奋又有点无从下手?看着官方文档里一堆 API 参数,想着怎么管理好几轮对话的上下文…...

3步实现路由器固件自动更新:从繁琐到智能的运维升级指南

3步实现路由器固件自动更新:从繁琐到智能的运维升级指南 【免费下载链接】immortalwrt An opensource OpenWrt variant for mainland China users. 项目地址: https://gitcode.com/GitHub_Trending/im/immortalwrt 一、痛点分析:为什么手动更新让…...

KeySim:3D键盘设计终极指南,免费打造个性化虚拟键盘定制体验

KeySim:3D键盘设计终极指南,免费打造个性化虚拟键盘定制体验 【免费下载链接】keysim design and test virtual 3d keyboards. 项目地址: https://gitcode.com/gh_mirrors/ke/keysim 想要设计独一无二的个性化键盘却担心成本太高?KeyS…...

QMCDecode终极指南:3分钟解锁QQ音乐加密文件,让音乐真正属于你!

QMCDecode终极指南:3分钟解锁QQ音乐加密文件,让音乐真正属于你! 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到…...

如何通过MCP协议实现AI助手与Figma设计的双向交互

如何通过MCP协议实现AI助手与Figma设计的双向交互 【免费下载链接】cursor-talk-to-figma-mcp Cursor Talk To Figma MCP 项目地址: https://gitcode.com/GitHub_Trending/cu/cursor-talk-to-figma-mcp 在当今的设计开发工作流中,设计工具与AI助手之间的割裂…...

OpenClaw跨平台测试:ollama-QwQ-32B在Mac/Win/Linux任务兼容性

OpenClaw跨平台测试:ollama-QwQ-32B在Mac/Win/Linux任务兼容性 1. 为什么需要跨平台测试? 上周我在团队内部推广OpenClaw时遇到一个典型问题:同事用Windows写的文件整理脚本,在我的Mac上运行时频繁报"路径不存在"错误…...

用于网页设计的 Claude Code

Claude Code 现在绝对算得上设计圈里最热的产品之一。它真正让人上头的地方,不是“会回答问题”,而是它能把你脑子里一个还没成型的想法,几分钟之内就往可实现的页面上推。也就是说,你不再只是停留在概念层,而是能很快…...

直接上代码!今天咱们用MATLAB整点好玩的——三种群智能算法在栅格地图路径规划的实战PK。先来张刺激的障碍地图热热身

三种优化算法(灰狼优化算法GWO,蜣螂优化算法DBO,麻雀搜索算法SSA)路径规划对比。 地图为20*20栅格地图(可自行更改),地图障碍物可以自定义,提供五个地图。 可替换为其它优化算法。 注…...

Flow3D 11.1玩转金属3D打印模拟】手把手教你搞熔池仿真

Flow3d 11.1 lpbf 熔池仿真模拟 slm 选区激光熔化 降价 与别的店大几百上千的基本一致 (视频是多层模拟的视频) 1.该模拟设包含颗粒床以及建立过程(有视频),运用Flow3D11.1、EDEM软件以及Gambit软件(含安装包)&am…...

双臂机器人Matlab仿真及程序源码

双臂机器人matlab仿真,程序源码,带注释,带轨迹规划。打开Matlab敲下第10086行机械臂仿真代码,突然发现让两个铁胳膊和谐共舞可比教人类小朋友握手难多了。今天就唠唠怎么用Matlab让双机械臂画出完美轨迹,重点看黄色告警…...

计及力累积效应电力变压器绕组短路强度与稳定性研究 电力变压器作为电网系统的电力转换枢纽

计及力累积效应电力变压器绕组短路强度与稳定性研究 电力变压器作为电网系统的电力转换枢纽,因短路冲击造成其损坏的事故时有发生,统计发现单次短路冲击有时并不会对绕组造成严重的损坏,但会存有难以检测的暗伤,经多次作用累积&am…...

comsol18650圆柱形电池组流体直冷热管理仿真 采用电化学-热-流场耦合/集总电池-流场...

comsol18650圆柱形电池组流体直冷热管理仿真 采用电化学-热-流场耦合/集总电池-流场耦合仿真模型 模拟电池组在充放电工况下,湍流流体介质直冷的散热模式下电池的电性能,热参数变化「这年头搞电池热管理,谁还没被18650的散热问题卡过脖子&…...

深圳龙岗企业周花哪个好

深圳龙岗企业周花哪个好?深圳皇家文化传媒有限公司值得关注在深圳龙岗,企业对于周花的需求日益增长,优质的企业周花不仅能够美化办公环境,还能提升企业形象。那么,深圳龙岗企业周花哪个好呢?深圳皇家文化传…...

OpenClaw任务调度:GLM-4.7-Flash定时执行方案

OpenClaw任务调度:GLM-4.7-Flash定时执行方案 1. 为什么需要定时任务调度 上周我需要每天凌晨自动生成一份技术日报,手动操作既耗时又容易遗忘。当我尝试用OpenClaw对接本地部署的GLM-4.7-Flash模型时,发现原生的对话式交互无法满足周期性任…...

python之with和try

with 和 try 都是 Python 中用于处理“可能会出问题”的场景的关键字,但它们的核心目标和应用方式有所不同。下面这个表格能帮你快速把握它们的核心区别和联系:特性with语句try语句核心目标资源管理,确保资源使用后被正确释放异常处理&#x…...

【超全】2026年3月OpenClaw(Clawdbot)京东云5分钟新手搭建流程

【超全】2026年3月OpenClaw(Clawdbot)京东云5分钟新手搭建流程。OpenClaw怎么部署?本文面向零基础用户,完整说明在轻量服务器与本地Windows11、macOS、Linux系统中部署OpenClaw(Clawdbot)的流程&#xff0c…...

智能商业洞察平台的多源数据融合:AI应用架构师的6个踩坑与解决方法

智能商业洞察平台的多源数据融合:AI应用架构师的6个踩坑与解决方法 一、引言 (Introduction) 钩子 (The Hook) 在当今数字化浪潮下,企业犹如置身数据的海洋,海量数据从各个业务系统、社交媒体、物联网设备等多源渠道滚滚而来。想象一下,作为 AI 应用架构师,负责构建智能…...

论人机协同中的模糊性与不确定性

在人工智能从"工具辅助"向"智能伙伴"演进的过程中,人机协同正突破传统"人主导-机执行"的单向模式,形成双向认知交互的新型协作关系。这种关系的复杂性远超简单的人机分工——人类认知的模糊性(Fuzziness&#…...

伐度司他(Vadadustat):透析患者肾性贫血口服新选择,告别注射更便捷

慢性肾脏病(CKD)患者,尤其是长期依赖透析的人群,肾性贫血是最常见且影响深远的并发症之一。传统治疗依赖注射促红细胞生成素刺激剂(ESA),不仅给药不便,还可能伴随血压波动、血栓风险…...