by Conmajia
⚠️ 由于安全设置 ,本文互动查询功能已失效
本文赞助者: ◎梦想起航◎ tzhang Tim 李敏 倾听 metoer
破除现代迷信 ,人人有责 。.NET 的源码点这里 Fork
奇了个怪
.NET 作为开发平台来说 ,不可否认是优雅的 、高效的 。可是有些人就是看不到它极高的开发效率 ,极短的市场推出时间 ,极强的扩展能力 ,非得在性能上较劲 ,把一帮小萌新忽悠得一说起 .NET 就觉着是“慢” 、“卡”的代名词 。
这些人总是存在一个思维误区 ,认为程序性能是和语言挂钩的 。您没事儿吧? 真要这样 , 《数据结构》 、 《算法》 这些课早他妈该撤了好吧 。汽车和飞机速度确实不一样 ,但就算给您一架私人飞机 ,您能给表演表演每天开飞机去买菜? 任何语言的性能瓶颈 ,大半还是出在使用这语言的人身上 。新手司机开着十辆布加迪也不可能拼过驰骋秋名山的五菱宏光老司机 。咱们不抬杠 ,今天就只说性能 ,不说开发效率 、上市速度 。非要跟我抬杠 ,算上这些 ,可能我的“低性能”产品已经出街了 ,你憋半天憋不出屁的“高性能”产品还在扑街 ,优越个🐔🎱呢 。
.NET 的开发语言一般用 C# ,后边我就不区分 .NET 和 C# 了 。F# 其实也差不多 ,反正最后都编译成 intermediate language ,可以认为是等价的 (函数式编程更多的是一种思想 ,这个话题我以后会写) 。实话实说 ,C# 这门编程语言 ,一开始就处于一个挺尴尬的位置 。性能上 ,有 C/C++ 在前边拦着; 应用上 ,有 Java 、Python 在后边堵截 ,不上不下 。.NET 玩家心里总憋着股邪火 ,只好左右互搏 ,自娱自乐了 。
语言的比较
这个问题的根源 ,很大一部分在于很多程序员只会一种语言 , 甚至连这唯一的语言也不够精通 ,所以才会要么看不起别的语言 ,要么埋怨自己这语言性能太弱 。想要一招鲜吃遍天 ,梦里可以 ,醒来最好还是把这茬给忘了 。而那些对 .NET 一知半解就开始嘲讽的“别的语言”玩家 ,就更别指望他们能理解到其中的精髓了 。
把自己限制于一种语言的程序员会经常错过其他地方提供的重要机会 。这样的程序员绝对不会被他们的老板或客户看成是专业领域中的专家 。
C# 虽然定位尴尬 ,但是它本身是高度优化的 ,要比性能 ,无非就 C/C++ 或者汇编才配跟它比 。Java 、Python 这些 ,大家都是 IL 解释的 ,本是同根生 ,相煎急锤子 ,都不一定能比过 C# 。C 系玩家就算了 ,特性使然确实比不过 ,但是事实上优化之后性能也大差不差 (我马上就能证明这一点) 。最奇怪的就是好多 J 、P 玩家居然好意思笑话 C# 性能辣鸡 ,完全没意识到自己那惨样儿 。就好像宋小宝去够姚明手里的球 ,结果潘长江在边儿上笑得挺欢 ,笑您🐴呢? 和机器码比起来 ,任何使用中间语言虚拟机/runtime 的语言都有损失 ,这话没人能反驳吧? Benchmark ,一般都是用最耗资源的操作——比如图像处理——来比较 。这无非就是测试磁盘 、内存的 IO 性能呗 。操作系统最终的性能在那卡着 ,assembly 可以达到 95% ,C 系 8 、90% ,C# 可能亏点儿 ,6 、70%的样子 。然而 ,这都不是重点 ,重点在于 ,你有没有完全利用到语言的全部特性 ,以及那些你认为低效率的算法 ,非要用它们真的大丈夫? 40 米大刀拿来削苹果 ,可能它的性能真的很低吧 。
我过去的试验
2012 年的时候我就测试过用 C# GDI+读写 bitmap ,一开始用的常规算法 ,很慢 。但是经过优化后 ,性能提升了 363 倍 ,我也没用别的任何类库 ,还是 .NET 2.0 原生 。您说怎么判断 C# 的性能到底是低还是高?
▲ 2012 年的测试 ,内容已经丢失 ,我在网上找到的爬虫文章
当时一帮哥们儿看了我的试验都手痒秀了一遍优化:
鼠标移到作者名字上可以查看作者信息 ,他们都是真·大神
▼ 各路大神和我试验的结果 (4096×4096@24bpp 反色)
编号 | 作者 | 耗时 (毫秒) | 实现方法 | 平台 |
a. | laviewpbt | 25 | 汇编 + PowerBasic | i3 380M/3GB/Win7 32-bit |
b. | 兰征鹏 | 12~19 | VC++.NET 调用 SSE 指令 | i7 860/12GB/Win7 64-bit |
c. | 胡飞 | 33 | 重写 C# 图像库 、unsafe 指针 | 优于 a |
d. | Conmajia | 46 | 原生 GDI+ 、unsafe 指针 | 同 a |
上面这张表 ,可以看到 ,几种语言的性能是接近的 ,相差最大 20 毫秒 (C# vs 汇编) 。除了 b 项 C++ 加了 buff (当时 i7 是顶级 CPU) ,换到同平台 ,基本差不多的 。当技巧和算法都优化到极致之后 ,其实 C# 、VB 、C++ 的性能非常接近了 ,轻微的差异在大部分场景下几乎是可以忽略的 。这一点 ,我以前这个试验就是很好的解释 。计算机技术发展到今天 ,语言的运行效率差异早就可以忽略不计了 ,方法论和算法的才是核心 。事实上 ,对现代 C# 而言 ,提高性能其实挺简单的 ,通过下面的试验你就可以看出来 。
重新再试验
有人说 ,跟程序员交流 ,不要说那么多话 ,要么秀代码 ,要么秀妹子 。我只有请出互联网的 first lady——瑞典模特 Lena Soderberg (展示的样本经过了处理) 。Lena 的介绍可以看昨天的新闻: 《花花公子》 的封面女郎 ,计算机图像界的女神 。显然当年的程序员们也是一手杂志一手纸躲被窝里撸管的主 。咦 ,我为什么要说“也”? 😅
Lena 是瑞典拼法 ,Lenna 是英文拼法 。
▲ Lena/Lenna
测试图我用 Lena 原图 ,超高清 3831×8192@24bpp ,文件大小 94.2MB 。完整的 样本可以在 lenna.org 下载 。
(曾经这里有张图😄)
拿 DIP 里最最简单的反色举例 ,同样用 C# ,一个萌新和一个大神分别实现 ,那最后的效率可差老了去了 。不信咱来试试 。 (例子使用的方法仅限演示 ,不代表标准方法 ,也不是我这篇文章要讲述的主题——它只是个例子)
萌新的实现: 单线程 、managed
虽然有点儿欺负人 ,但它确实是实际存在的现象 。新手玩家不管是熟练度还是连招都跟老油条没得比 。
private void invert(Bitmap bmp) {
Color sc = new Color();
for (int i = 0; i < bmp.Width; i++) {
for (int j = 0; j < bmp.Height; j++) {
sc = bmp.GetPixel(i, j);
bmp.SetPixel(i, j, Color.FromArgb(0xff - sc.R, 0xff - sc.G, 0xff - sc.B));
}
}
}
这种做法理论上是没错的 ,也能实现效果 ,就是太慢了 。于是萌新可能就开始骂街: C# 真辣鸡 ,.NET 真辣鸡 。可是这算法就他妈的辣鸡啊! 这种单线程的 managed 语法 ,想快它也快不起来啊 。
▲ 你来告诉我 1 个核上慢慢摇性能怎么提起来?
大神的实现: 并行计算 、unsafe
大神之所以能叫大神 ,因为他们可以利用语言以及计算机的全部实力 。思路上 ,他们能够选用最佳的合适算法; 技巧上 ,他们可以挖掘电脑的所有潜力 。同样的语言 ,不同的性能 ,我再次提出那个问题: 你说瓶颈在语言还是在人?
private void invert(Bitmap bmp) {
BitmapData bdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
Parallel.ForEach(Partitioner.Create(0, bdata.Height), (H) => {
unsafe {
int x, y, w, h, s;
byte * s0, p;
w = bdata.Width;
h = bdata.Height;
s = bdata.Stride;
s0 = (byte * ) bdata.Scan0;
for (y = H.Item1; y < H.Item2; y++) {
p = s0 + y * s;
for (x = 0; x < w; x++) {
* p = (byte)(0xff - * p);
p++;* p = (byte)(0xff - * p);
p++;* p = (byte)(0xff - * p);
p++;
}
}
}
});
bmp.UnlockBits(bdata);
}
注意这个实现用到了并行计算 ,没有用 SetPixel
而是直接用指针操作的 ,虽然有点像 C/C++ ,但是依然属于原生 C# 的范畴 。可是我不会逐条讲解为什么用了这些技术性能就可以提高 。我这文章的主题不是教你怎么用 Parallel
或者给你对比单线程和多线程的性能差 ,而是演示给你看初哥和大手子能轻易拉开多大的距离 ,以此反驳那些动不动觉得某某语言性能不行的门外汉 。我可不负责科普教学 ,那是你自己解决的事儿 。
萌新 vs 大神
▲ 同一种语言 ,同一个任务
看看结果吧! 42466ms 对上 101ms ,420 倍的差距 。谁跟我说 .NET 性能差来着? 请大点儿声 ,谢谢! 🙏
完毕
其实就上面的例子来说 ,还能把性能再往上提 ,但这都是次要的 。我想表达的意思已经表达完了: .NET 的性能并不低 ,如果你觉得低 ,那可能是因为你的能力低 。
The End. □
僵尸兴奋地打开了你的头盖骨 ,又失望地走开了 。旁边的屎壳郎眼前一亮 。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验