C#中的循环反转加速应用程序

时间:2010-03-05 04:55:17

标签: c# performance optimization loops

我们正在使用EmguCV开发视频处理应用程序,最近不得不做一些像素级操作。我最初编写循环来遍历图像中的所有像素,如下所示:

for (int j = 0; j < Img.Width; j++ )
{
    for (int i = 0; i < Img.Height; i++)
    {
        // Pixel operation code
    }
}

执行循环的时间非常糟糕。然后我在EmguCV论坛上发布了一个建议来切换这样的循环:

for (int j = Img.Width; j-- > 0; )
{
    for (int i = Img.Height; i-- > 0; )
    {
        // Pixel operation code
    }
}

我很惊讶地发现代码执行的时间是一半

我唯一能想到的是每次访问一个属性时循环中发生的比较,它不再需要。这是加速的原因吗?或者还有其他什么?我很高兴看到这种改善。如果有人能澄清其原因,我会很高兴。

4 个答案:

答案 0 :(得分:14)

差异不在于分支的成本,而是在内部循环中提取对象属性Img.WidthImg.Height。优化器无法知道这些是用于该循环的常量。

通过执行此操作,您应该获得相同的性能加速。

const int Width = Img.Width;
const int Height = Img.Height;
for (int j = 0; j < Width; j++ )
{
    for (int i = 0; i < Height; i++)
    {
        // Pixel operation code
    }
}

编辑:

正如Joshua建议的那样,将Width放在内部循环中会让你顺序遍历内存,这将是更好的缓存一致性,并且可能更快。 (取决于你的位图有多大)。

const int Width = Img.Width;
const int Height = Img.Height;
for (int i = 0; i < Height; i++)
{
    for (int j = 0; j < Width; j++ )
    {
        // Pixel operation code
    }
}

答案 1 :(得分:13)

我假设您使用的是System.Drawing.Image类?看一下.Width和.Height的实现,我看到他们在GDI +中执行函数调用(gdiplus文件中的GdipGetImageHeight和GdipGetImageWidth),这看起来相当昂贵。

通过倒退,您可以进行一次调用,而不是每次迭代。

答案 2 :(得分:3)

不是循环反转加速了事情 - 事实上你访问宽度和高度属性的次数要少得多。

答案 3 :(得分:1)

这是因为CPU就像曲棍球运动员一样,后退时会更快; - )

更严重:
这与循环的方向无关,而是与原始构造中的循环控制条件暗示取消引用Img对象以索引其宽度或高度属性(为每个和循环中的单次迭代),其中第二个构造仅评估这些属性一次 此外,新条件针对值0进行测试的事实甚至可以节省立即值的加载。 这可能解释了差异(假设在内部完成的工作相对最小,即+/-与测试Object.Property的工作相同,因为您指示大约50%的增益)。

修改
请参阅Michael Stum的回答,其中指出 Img.Width / Height参考比想象的更昂贵。由于它有时会发生属性,对象的实现可能会运行大量的代码来生成值(例如,每次都可以做一堆数学来达到宽度,而不是以某种方式缓存它等等。 )。这个Img对象似乎就是这种情况,因此有兴趣只执行一次(如果你确定该值在循环逻辑的持续时间内保持不变)。