【问题标题】:Fastest Array addressing最快的数组寻址
【发布时间】:2012-01-19 05:35:00
【问题描述】:

我正在对存储图像信息的数组运行图像分析代码。不幸的是,代码非常繁重,运行一帧平均需要 25 秒。我看到的主要问题是数组寻址。哪个是通过 2d 数组最快的,并且有任何差异

先水平后垂直

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

垂直然后水平?

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]

非常感谢任何帮助。 最大

【问题讨论】:

  • 我应该提到该数组实际上是用于位图颜色分配的 BitmapData:/ sry...
  • 那么,你已经在固定内存了吗?
  • 您是否尝试过编写每个解决方案并测量需要多长时间?那会给你最准确的答案。但如果我不得不猜测,我会说选项 3 和 4 可能比选项 1 和 2 稍快。
  • 如果单张图片需要 25 秒,那么您发布的代码片段显然不是限制部分。
  • 您最大的问题是使用多维锯齿状数组。你能把它变成一个基于零的单维数组吗?

标签: c# arrays performance pointers multidimensional-array


【解决方案1】:

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

通常,我们希望内部扫描是水平的(就阵列而言,而不是图像,尽管对于大多数格式来说都是相同的)。原因是这样的数组:

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. 检索值。

对于后者,逻辑是:

  1. 检查边界
  2. 通过指针获取数组。
  3. 检查边界
  4. 检索值。

还有不太好的内存缓存命中频率。当需要“锯齿状”结构时,后者有好处,但这里不是这种情况。 2D 几乎总是比数组数组快。

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

您可能会从执行 1d 2d 逻辑中得到提升。有一个单维数组,其中 idx = y * width + x。它应该不会产生明显的差异,但值得一试。

优化确实会尝试提升对 .Length 的调用并省略不必要的边界检查,因此您可能会发现手动提升和切换到指针算法并没有任何好处,但在您确实需要减少时间的情况下这当然值得分析。

最后。你有没有分析过你的代码在扫描数组并且什么都不做的时候有多快?可能代码的另一部分是真正的瓶颈,而您正在修复错误的东西。

【讨论】:

  • 除非在最近的 .NET CLR 中发生了变化,否则 .NET 中的矩形数组一直是出了名的慢,而且加速通常是相反的方向(从 x[,]x[][]),而不是比这里建议的方向。
  • .NET 实现的一个问题是矩形数组可以有非零基数,这会使许多核心操作复杂化。更多详细信息:blog.mischel.com/2013/05/08/…
【解决方案2】:

一种选择是使用反向循环(从array.Length 开始你的for() loop 到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]

【讨论】:

  • 这将如何加快速度?编译器应该足够聪明,只访问一次属性,因为数组长度在此期间不会改变。
  • link 读这个,例如
  • 该链接适用于 Java,而不是 .NET,因此不保证适用。另外,it seems there is some disagreement。我想这值得一试,因为只需一分钟即可试用。
  • 该链接用于java; JIT(不是 C# 的一部分,而是:VM)通常可以发现一个常规向量 for 循环并删除边界检查等......我认为这需要更清晰的分析来支持它,个人。
  • 也许是 Max z。可以帮我们检查一下。
【解决方案3】:

我不知道,但你已经想出了例子。因此,您可以在循环中运行您的代码示例并自己分析它。

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

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

【讨论】:

    【解决方案4】:

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

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

    有一个代码示例。

    【讨论】:

    • int*(问题中)的示例已经这样做了。另请注意,JIT 通常能够删除对 vector/for 循环的边界检查。
    【解决方案5】:

    如果可能,请尝试重新分配您的数组,使第一个维度小于第二个维度。它会大大加快速度。 另一种解决方案是按照上面的建议将数据重新分配到一维数组中。

    【讨论】:

      【解决方案6】:

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

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

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

      如果单帧需要 25 秒,那么它要么是 huuuuge,要么你做了非常复杂的处理。在这种情况下,如果您为每个输出像素访问许多输入像素,那么花精力优化内存访问才是有意义的。

      【讨论】:

      • 两者...它使用 FFT 和过滤器进行深度分析
      猜你喜欢
      • 2011-06-02
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 2013-05-31
      • 2012-07-19
      • 2023-04-07
      • 2013-11-25
      • 2011-12-02
      相关资源
      最近更新 更多