一、测试结果汇总
将前面的测试结果进行汇总,整理为表格(单位是毫秒,数值越小越好)——
测试 | f0_if | f1_min | f2_neg | f3_sar | f4_mmx | f5_sse |
VC6 on 32bit | 2016 | 2063 | 719 | 672 | 37.5 | 25.7 |
VC6 on 64bit | 2028 | 2075 | 718 | 687 | 37.1 | 25.3 |
VC2010(32) on 32bit | 1793 | 2112 | 512 | 437 | ||
VC2010(32) on 64bit | 1716 | 2106 | 514 | 437 | ||
VC2010(64) on 64bit | 1623 | 1997 | 421 | 328 | ||
C#2010(any) on 32bit | 1922 | 2147 | 2471 | 559 | ||
C#2010(any) on 64bit | 1871 | 1918 | 2406 | 651 | ||
C#2010(x86) on 32bit | 1930 | 2142 | 2462 | 545 | ||
C#2010(x86) on 64bit | 1829 | 2073 | 2340 | 516 | ||
C#2010(x64) on 64bit | 1865 | 1911 | 2399 | 651 | ||
VB6 on 32bit | 2844 | 1078 | ||||
VB6 on 64bit | 2839 | 1061 |
注:CPU:Intel Core i3-2310M, 2100 MHz。
对应图表——
上图是以毫秒为单位,因数据量很多,看起来有点吃力。于是又做了一个表格,将各列的数据除以“f0_if”列,即以倍数为单位,数值越大越好——|
测试 | f0_if | f1_min | f2_neg | f3_sar | f4_mmx | f5_sse |
VC6 on 32bit | 1.00 | 0.98 | 2.80 | 3.00 | 53.76 | 78.44 |
VC6 on 64bit | 1.00 | 0.98 | 2.82 | 2.95 | 54.66 | 80.16 |
VC2010(32) on 32bit | 1.00 | 0.85 | 3.50 | 4.10 | ||
VC2010(32) on 64bit | 1.00 | 0.81 | 3.34 | 3.93 | ||
VC2010(64) on 64bit | 1.00 | 0.81 | 3.86 | 4.95 | ||
C#2010(any) on 32bit | 1.00 | 0.90 | 0.78 | 3.44 | ||
C#2010(any) on 64bit | 1.00 | 0.98 | 0.78 | 2.87 | ||
C#2010(x86) on 32bit | 1.00 | 0.90 | 0.78 | 3.54 | ||
C#2010(x86) on 64bit | 1.00 | 0.88 | 0.78 | 3.54 | ||
C#2010(x64) on 64bit | 1.00 | 0.98 | 0.78 | 2.86 | ||
VB6 on 32bit | 1.00 | 2.64 | ||||
VB6 on 64bit | 1.00 | 2.68 |
对应图表(因MMX/SSE的性能太鹤立鸡群,影响图表阅读,故省略)——
二、各算法的性能分析
现在我们按照列顺序(即各个算法的顺序)来进行分析——
1.首先,f0_if是采用if语句的基础算法,当作标杆用来评比其他算法。
2.f1_min采用了系统函数min、max。因现代编译器会对min、max做内联优化的,理论上应该与f0_if差不多。但实际测试后发现,f1_min比f0_if慢了一些。原因可能是因为min/max的语义与if不同,编译优化的深度不及if语句。
3.f2_neg算法采用了位运算来避免分支跳转,处理速度有了较大提高,平均性能大约是f0_if的3倍。可惜C#不支持将bool型强制转换为整型,而调用Convert.ToInt16带来了性能开销,使其比f0_if还慢。
4.f3_sar算法利用带符号移位避免了状态寄存器的访问,处理速度又有了一定的提升,平均性能大约是f0_if的3.5倍。
5.f4_mmx、f5_sse算法因为用到MMX、SSE这些SIMD指令集,所以处理速度比前面那些高级语言算法高了一个级别。
算法 | 最小倍数 | 最大倍数 | 平均倍数 | 备注 |
f1_min | 0.81 | 0.98 | 0.91 | 无VB6 |
f2_neg | 2.64 | 3.86 | 3.09 | 无C# |
f3_sar | 2.86 | 4.95 | 3.52 | 无VB6 |
f4_mmx | 53.76 | 54.66 | 54.21 | 仅VC6 |
f5_sse | 78.44 | 80.16 | 79.3 | 仅VC6 |
三、其他发现
分析测试结果,还发现——
1.VC2010的的编译优化能力比VC6要强,同样程序的运行速度要快很多。这可能是因为VC2010的编译优化更能适应最新处理器的指令级并行性。
2.在64位系统中能运行32位程序,除了C#以外,其运行速度与在32位系统中的差不多。
3.C#2010的程序,有时在64位系统上的运行速度比32位系统还慢,甚至包括了“x64”平台方式编译的程序。似乎“x86”平台方式编译的程序的平均性能最好。可能是因为64位JIT(即时编译器)不如32位的成熟。
4.对于VC2010编译器来说,编译的64位程序比32位程序的运行速度有比较明显的提升。看来编写64位程序的最好还是用VC等C/C++编译器。
5.在传统印象中,VB、C#的运行速度比C语言慢很多。但通过这次测试发现,虽然它们的性能是有一定差距,但是是处于同一级别的,甚至C#与VC6打平还略占优势。影响程序运算速度的最关键因素是算法,用编译能力最差的VB6实现的f2_neg,比编译能力最好的VC2010实现的f0_if要快得多。
四、总结
使用位掩码代替分支能带来较大的性能提升,而且该方案能推广到任何高级语言,能充分利用高级语言的可移植性,能满足大多数的性能优化需求。
但当对性能要求极高、且不要求可移植性时,强烈推荐MMX、SSE等SIMD指令集,能使处理速度提高一个级别。
附:测试程序打包下载——
https://files.cnblogs.com/zyl910/noif_Test.rar
http://dl.dbank.com/c069c6thd7
(完)
《深入探讨用位掩码代替分支》系列文章——
1、利用带符号移位生成掩码:http://www.cnblogs.com/zyl910/archive/2012/03/12/noifopex1.html
2、汇编代码分析:http://www.cnblogs.com/zyl910/archive/2012/03/21/noifopex2.html
3、VC6速度测试:http://www.cnblogs.com/zyl910/archive/2012/03/27/noifopex3.html
4、VC2010速度测试:http://www.cnblogs.com/zyl910/archive/2012/03/28/noifopex4.html
5、C#2010速度测试:http://www.cnblogs.com/zyl910/archive/2012/03/29/noifopex5.html
6、VB6速度测试:http://www.cnblogs.com/zyl910/archive/2012/03/30/noifopex6.html
7、MMX指令集速度测试:http://www.cnblogs.com/zyl910/archive/2012/04/09/noifopex7.html
8、SSE指令集速度测试:http://www.cnblogs.com/zyl910/archive/2012/04/12/noifopex8.html
9、测试成绩总结:http://www.cnblogs.com/zyl910/archive/2012/04/13/noifopex9.html