在为图像处理项目选择C#和C ++时,我应该考虑哪些因素?

时间:2011-03-24 13:31:58

标签: c# c++ performance

我想开发一些图像处理代码,我想知道在C ++和C#中开发它们之间是否存在很大差异?

是否有任何详细文档解释了在C#中实现哪些好处以及在C ++中实现什么是好的?

据我所知,由于C#代码在运行之前编译为机器代码(使用.NET CLR JIT编译器),如果在代码开发过程中,两种语言之间应该没有太大区别,你看看特定语言建议实现设计模式(例如,使用大量新的而不是使用固定数组)。

编辑:   还有一些其他参数我一开始并没有想到,但是当我读到一些答案时,我正在看现在: 1-这是一个高级项目,这意味着我可以要求用户拥有一台非常好的计算机(大量的内存和多核处理器) 2-我可以假设用户有一个非常好的图形卡,所以我可以使用它的GPU进行处理。 3-我认为WPF有利于这一发展(我是对的!)。是否有类似的C ++库?我曾与MFC合作,但我不确定在处理需要显示图像的项目时,MFC和WPF一样好,而且GUI非常重要。

5 个答案:

答案 0 :(得分:5)

.NET IL被编译为机器代码,就像C ++一样。但几乎总是C ++代码会更快,因为:

  • 编译时间成为运行时的一部分(仅限一次,但有时这是一个问题)
  • 由于上述原因,JIT编译器在优化上花费的时间少于AOT编译器。因此,JIT生成的代码通常较慢。例如,JIT通常不会对任何内容进行矢量化,这对图像处理很重要。大多数情况下,由于VM限制,甚至无法生成矢量化代码(Mono.Simd是一个例外)
  • VM中的语言必须确保程序不会逃离VM。例如,他们必须在每次访问时检查数组边界(除非编译器能够证明索引总是在范围内;请记住,JIT编译器不会花太多时间在这样的分析上)。
  • 对于时间要求严格的内核,您可以自己插入汇编程序指令。 VM不允许这样做,因为汇编程序可能会中断VM。

在某些情况下,托管代码可能比非托管代码更快:

  • garbarge收集器允许比malloc更有效地分配内存。但是,除非记忆只是短时间使用,但Mark-And-Sweep也需要一些时间。
  • JIT可以在代码上编译具有更多静态假设的代码(比如,它知道虚函数的所有可能实现),并在假设不再存在时重做编译。这是在Java HotSpot中完成的,但不是在Microsoft的JIT和Mono中完成的。

总结:如果执行速度很重要,请使用C ++。但是,正如Paint.NET所示,C#可以快速足够。还要考虑主要使用C#进行编码的选项,一旦您对某些重要函数的速度不满意,请在C++/CIL重新实现它,或使用P/Invoke调用优化函数。

答案 1 :(得分:3)

作为将图像处理库从C ++转换为C#并且完成基准测试的人,语言对你正在做的事情的速度几乎没有影响,更重要的是你正在使用的算法以及你正在使用的算法。操纵图像(即在Windows中使用位图上的锁定位)。我设法获得了显着的速度提升,因为我可以在C#中比在C ++中更容易编写更多的并行代码,而且不能仅仅使用Monitor.Wait(我承认我不是C ++线程专家,实际上我不是一个C ++线程业余爱好者)但是有很多选项可以轻松地并行化操作(在图形中非常内在),从C ++转换到C#时加速了大量的速度(加上只需要担心关于释放资源而不是记忆使生活变得更加轻松。显然,如果你对C ++非常熟悉,那么你可以用C ++编写更快的代码,但是你知道C ++和SIMD可以胜过C#编译器。我还应该注意到,我们从C ++转换为C#,假设我们丢失大约10%的速度,我们转换的原因是添加新功能的时间成本太高以至于重写为C#变得值得我们,最终产品最终更快只是一个奖励(现在我也应该提到原始库是在SIMD扩展之前编写的,其中添加到Visual C ++,因此使用它是原始库作者无法使用的)。

答案 2 :(得分:1)

虽然你认为.NET代码被JIT编译为机器代码是正确的,但你必须意识到你几乎无法控制这个过程。你没有选择目标指令集,而托管代码主张声称JIT编译是优越的,因为它可以使用每台机器上可用的最快指令集,事实是这还没有实现

结论:您可以使用SSE指令编写C ++代码,这些指令的运行速度比同一CPU上JIT生成的代码快4倍。


其他一些值得一提的事情:SIMD并行性比编写更容易编写,编译器更容易优化。 SIMD没有开销,但线程同步不是免费的。使用SIMD的隐藏陷阱较少,因此获得线性加速更加容易。多线程容易陷入缓存线共享和争用。托管语言让您无法控制虚假共享。

是的,C#可以更轻松地进行多线程处理。糟糕。 C ++使得正确执行多线程变得更加可行,并且您可以从调整单线程代码中获得足够的速度,甚至不需要处理线程的额外复杂性。

如果你决定优化自己,这里有一些技巧:(一旦你达到了预期的性能,就停在线上的任何一点,因为你应用的越多,你的代码就越复杂)

  1. 处理原始数据时,不要使用一些在一堆像素上添加间接和抽象的API。这不仅可以减慢你的速度,还会干扰下一堆改进。 (Kris alluded to LockBits vs SetPixel,原始图片访问的原生例程为GetDIBits

  2. 缓存调整。一种简单的方法是将最内层的循环展开16倍左右。然后反转循环嵌套顺序。这将导致处理数据的矩形块,这些块通常是相互依赖的,因此您可以为每个缓存填充多次访问每个数据块。但是,在此阶段使用缓存分析器是一种浪费。

  3. 使用SIMD。在每次循环迭代中处理4个或更多倍像素。轻松获胜。特别是因为你已经稍微展开了循环,那些相同的指令只是坐在那里乞求合并。但是,除非你摆脱抽象,否则无法完成。

  4. 现在使用缓存分析器。 SIMD提取与标量指令具有明显不同的特征,因此您应该在SIMD转换后执行此操作。

  5. 也许多线程。如果问题真的很大。请特别注意缓存行大小。并尝试永远不要写入其他线程也使用的数据。绝对不要从多个线程写入相同的数据。

答案 3 :(得分:0)

这取决于你对这两种语言有多好,如果你真的是图像处理编程或算法的专家,c ++将为你的优化提供更多的自由。但如果你不是那么专家,那就使用更高级别的语言来保护自己

答案 4 :(得分:0)

有几个因素需要考虑......

是否有任何您可能想要使用的库 - 如果存在并且它们是.Net则可能会影响您支持C#的决策。

然而,我认为可以肯定地说,大多数密集型数字应用程序(以及图像处理基本上是在2D矩阵上的数字密集型操作)将用本机代码编写。有一个非常好的例外 - 看看Paint.Net,它几乎完全用托管代码编写,并且具有出色的性能和功能(http://getpaint.net)。

您申请的目的是什么?如果它是一种“爱好”或学习练习,那么请选择您最开心的语言。如果它是商业应用程序,我会首先查看库选项。