最快的阵列寻址

时间:2011-12-13 10:29:45

标签: c# arrays performance pointers multidimensional-array

我在存储有关图像信息的数组上运行图像分析代码。不幸的是,代码非常繁重,平均需要25秒来运行单帧。我看到的主要问题是数组寻址。哪个是最快的2d阵列,并且

中存在任何差异

水平然后垂直

for (int y = 0; y < array.Length; ++y)
    for (int x = 0; x < array[].Length; ++x)
        //Code using array[y][x]

和垂直然后horrizontal?

for (int x = 0; x < array[].Length; ++x)
    for (int y = 0; y < array.Length; ++y)
        //Code using array[y][x]

此外,我试图避免直接寻址并改为使用指针。

for (int y = 0; y < array.Length; ++y)
    int* ptrArray = (int*)array[0];
    for (int x = 0; x < array[].Length; ++x, ++ptrArray)
        //Code using ptrArray for array[y][x]

for (int x = 0; x < array[].Length; ++x)
    int* ptrArray = (int*)array[0];
    for (int y = 0; y < array.Length; ++y, ptrArray += array[].Length)
        //Code using ptrArray for array[y][x]

非常感谢任何帮助。 最大

6 个答案:

答案 0 :(得分:2)

一种选择是使用反向循环(从for() loop开始将array.Length降低到0)

这会让事情变得更快。

例如,

for (int x = array[].Length-1; x >= 0; --x)
    int* ptrArray = (int*)array[0];
    for (int y = array.Length-1; y >= 0 ; --y, ptrArray += array[].Length)
        //Code using ptrArray for array[y][x]

答案 1 :(得分:2)

最重要的规则是,在你描述之前,这都是理论。我并不认为那些坚持分析就是一切的人(没有一些理论,你不比货物崇拜者把椰子放在他们的耳朵上等待飞机的到来更好),但你的理论总是错误的或不完整的,所以分析是至关重要的。

通常,我们希望内部扫描是水平的(就数组而言,而不是图像,但对于大多数格式来说是相同的)。原因是使用如下数组:

00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29

它将被列为:

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

您希望沿着可以加载到CPU缓存中的连续块进行扫描,然后完全使用,而不是逐块扫描并需要定期更改CPU缓存内容。

如果您尝试并行化算法,这一点就更为重要。你希望每个线程处理它自己的连续内存块,就输入和输出而言,而不仅仅是遭受单线程代码对缓存命中频率差的影响,同时也会导致彼此的缓冲区变脏需要提神。这可能是并行化导致速度提升和并行化实际上减慢速度之间的差异。

另一件事是二维数组byte[,]而不是数组byte[][]之间的区别,你在“array [y] [x]”问题中的评论让我想知道也许你正在使用。前者获得arr [1,2]的逻辑是:

  1. 检查边界
  2. 计算位置(简单快速算术)
  3. 检索价值。
  4. 对于后者,逻辑是:

    1. 检查边界
    2. 通过指针获取数组。
    3. 检查边界
    4. 检索价值。
    5. 内存缓存命中频率也不太好。当需要“锯齿状”结构时,后者有好处,但这不是这种情况。 2D几乎总是比数组数组快。

      我认为不太可能提供帮助的事情,但我肯定会在你的情况下尝试:

      你可以从1d&lt; =&gt;中获得提升。 2d逻辑。有一个单维数组,其中idx = y * width + x。它不应该有明显的区别,但值得尝试。

      优化尝试同时提升对.Length的调用并省略不必要的边界检查,因此您可能会发现手动提升并切换到指针算术没有任何收获,但在您确实需要带来的情况下时间到了它当然值得描绘。

      最后。您是否了解过代码在扫描阵列和无所事事时的速度有多快?可能是代码的另一部分是真正的瓶颈,而你正在修复错误的东西。

答案 2 :(得分:1)

我不知道,但你已经提出了这些例子。因此,您可以在循环中运行代码示例并自行进行配置。

var sw = new Stopwatch();
sw.Start();
ExecuteMyCode();
sw.Stop();
Console.WriteLine("Time: " + sw.Elapsed);

您可以使用多线程构造like Parallel.ForEach来加快处理速度。如果循环中的代码避免了循环迭代之间的依赖关系,那么这将很有效。

答案 3 :(得分:0)

你可以不安全吗?指针。数组的问题是你仍然对每次访问都进行边界检查。指针删除它。请注意,这完全是C#支持 - 但您需要将其放入不安全的块中。这也意味着你必须能够运行不安全的代码,这并不总是给定的。

http://msdn.microsoft.com/en-us/library/28k1s2k6.aspx

有一个代码示例。

答案 4 :(得分:0)

如果可能,请尝试重新分配数组,以使第一个维度小于第二个维度。它会大大加快速度。 另一种解决方案是如上所述在单维数组中重新分配数据。

答案 5 :(得分:0)

始终确保最内层循环访问连续内存。

这通常是图像的一行。请注意,在矩形数组中,您应该将其设为最后一个索引:array[y,x]

this paper表明内置的C#矩形数组(带有多个索引)非常慢。我以前读过这个,但这是我得到的唯一参考。我将从一个线性数组开始,并为每一行计算一次偏移量。非托管只会帮助您处理非常琐碎的案件。

如果一个帧需要25秒,那么它就是huuuuge,或者你进行非常复杂的处理。在这种情况下,如果为每个输出像素访问许多输入像素,那么花费精力优化内存访问是很有意思的。