2011-03-21 88 views
16

我知道不安全的代码更适合访问像Windows API之类的东西,并执行不安全的类型转换,而不是编写更高性能的代码,但我想问问你是否注意到任何重大的性能改进与安全的c#代码相比,使用它的真实世界的应用程序。真正不安全的代码性能

+0

P/Invoke与'unsafe'不太一样......我不确定推理如下...另外:你是否测量了一下,看看你是否在做一些有用的事情? – 2011-03-21 07:21:02

+1

既不安全也不安全的应该是事实上更高性能的。总体性能取决于您在代码中实现的算法。 – zerkms 2011-03-21 07:22:11

+2

我现在没有使用不安全的代码。我只是想了解是否值得将代码的关键部分更改为不安全的代码。 – Miguel 2011-03-21 07:23:49

回答

13

一个很好的例子是图像处理。通过使用指向字节的指针(这需要不安全的代码)来修改像素速度要快很多。

实施例:http://www.gutgames.com/post/Using-Unsafe-Code-for-Faster-Image-Manipulation.aspx

如此说来,对于大多数情况,差异不会像明显。因此,在使用不安全的代码之前,请对应用程序进行配置以查看性能瓶颈的位置,并测试不安全的代码是否是真正的解决方案,以使其更快。

+5

安全图像处理也可以相当快。特别是如果您的算法可以写入以消除边界检查。我只使用不安全的代码将数据从位图复制到数组中并返回。如果你使用byte [],你甚至可以避免使用'Marshal'函数。 – CodesInChaos 2011-03-21 08:40:37

+1

@ Botz3000:这是一个很好的链接(期待阅读网站的其余部分),但结论是不健全的。使用'GetPixel'和'SetPixel' _is_真的很慢,但使用'int []'或多或少像使用没有缺点的指针一样快。 – 2012-07-10 09:01:06

+0

@CodeInChaos:我必须同意。我得出的结论是唯一受益的方面是复制位图数据。尽管如此,还没有尝试过使用'元帅'。 – 2012-07-10 09:02:28

13

正如其他帖子所述,您可以使用可以在非常专业的环境中使用不安全的代码,以获得显着的性能提升。其中一种情况是迭代数组值类型。使用不安全的指针运算比使用for循环/索引的通常模式快得多..

struct Foo 
{ 
    int a = 1; 
    int b = 2; 
    int c = 0; 
} 

Foo[] fooArray = new Foo[100000]; 

fixed (Foo* foo = fooArray) // foo now points to the first element in the array... 
{ 
    var remaining = fooArray.length; 
    while (remaining-- > 0) 
    { 
     foo->c = foo->a + foo->b; 
     foo++; // foo now points to the next element in the array... 
    } 
} 

这里的主要好处是,我们已经削减了数组索引完全检查..

虽然很高性能,这种代码很难处理,可能是相当危险的(不安全),并且破坏了一些基本的准则(可变结构)。但是,确实有一些场景中,这是合适的......

+4

这种代码的另一个重大缺陷是C#程序员中很大一部分人不了解它或其含义。如果你在一个团队工作,采用KISS原则... – MattDavey 2011-03-21 09:12:02

+6

你确定这个特定的例子更快吗? Sun的JVM使用指针算法使这种类型的代码(Java或Scala)几乎与C++一样快;我很惊讶C#实现不会这么做。 – 2011-03-21 16:16:45

+2

这个特殊的例子,不,因为编译器可以确定** i **永远不会超出数组的边界,因此跳过数组边界检查。但原则依然存在。在我的具体情况下,我有一个使用数组实现的环形缓冲区,以及一个迭代它的独立Iterator对象。在这种情况下,编译器无法进行此优化.. – MattDavey 2011-03-21 16:58:52

20

一些性能测量

的性能优势并不像你想象的那么大。

我做了一些正常的托管数组访问与C#中的不安全指针的性能测量。从Visual Studio 2010,.NET 4之外运行构建


结果,使用 任何CPU |发布基于以下PC规格:基于x64的PC,1个四核处理器。 Intel64家族6型号23步进10 GenuineIntel〜2833Mhz

Linear array access 
00:00:07.1053664 for Normal 
00:00:07.1197401 for Unsafe *(p + i) 

Linear array access - with pointer increment 
00:00:07.1174493 for Normal 
00:00:10.0015947 for Unsafe (*p++) 

Random array access 
00:00:42.5559436 for Normal 
00:00:40.5632554 for Unsafe 

Random array access using Parallel.For(), with 4 processors 
00:00:10.6896303 for Normal 
00:00:10.1858376 for Unsafe 

注意,不安全*(p++)成语居然跑慢。我猜想这打破了一个编译器优化,它结合了循环变量和安全版本中的(编译器生成的)指针访问。

源代码可在github

+2

-1。当时已经是一个很好的例子,因为基本上这个试用代码太简单了,怎么不衡量性能。 – TomTom 2014-02-15 09:11:29

+0

@TomTom如何? – 2014-02-17 10:28:13

+4

这太微不足道了。它没有考虑编译器可以/可以做的优化。因此,这些数字是否有任何意义尚不清楚。 – TomTom 2014-02-17 10:33:34

1

嗯,我建议阅读这个博客员额:MSDN blogs: Array Bounds Check Elimination in the CLR

这澄清越界检查如何在C#中进行。而且,Thomas Bratts测试对我来说似乎没用(看代码),因为JIT无论如何都会在他的“保存”循环中删除绑定检查。

+2

你可以在这里总结这篇文章。如果链接变黑,你的回答不会很有帮助。 – ChrisF 2012-09-30 20:32:58

+3

您已经错过了测试的要点 - 它们不会显示边界检查与不安全的效果。他们要证明边界检查经常被优化,而且在这些情况下不安全的代价可能是不值得的。 – 2013-03-25 15:57:48

0

我对视频操作代码使用不安全的代码。 在这样的代码中,您希望它在没有对值进行内部检查的情况下尽可能快地运行。没有不安全的属性,我可能无法跟上30fps或60 fps的视频流。 (取决于使用的相机)。

但是由于速度它被代码图形的人广泛使用。