【问题标题】:Performant Concatenating 2D Arrays stored as sub-arrays [closed]存储为子数组的高性能连接二维数组[关闭]
【发布时间】:2021-05-25 04:49:04
【问题描述】:

假设我们有几个 3x3 2D 数组,它们由 X Y 坐标标识。

伪代码:

Array 0-0     Array 1-0
[[1,2,1]      [[1,2,1]  
 [2,3,2]       [2,3,2]
 [3,1,2]]      [3,1,2]]
 
Array 0-1     Array 1-1
[[1,2,1]      [[1,2,1]  
 [2,3,2]       [2,3,2]
 [3,1,2]]      [3,1,2]]

我们还有一个索引数组,用来索引所有这些子数组。

chunk_array = [
   [Array 0-0, Array 0-1],
   [Array 1-0, Array 1,1]
]

问题是如何快速创建一个包含所有子数组的 6x6 最终数组,而无需迭代数组的所有元素,因为这是一个非常关键的地方,必须在尽可能短的时间内进行操作。

所以问题:执行此操作的最快方法是什么?我查看了 Buffer 副本,但仍然不确定这里最好的方法是什么。

【问题讨论】:

  • 如果时间紧迫并且您可以选择,我建议您不要实现自己的 N 维数组。使用一个库,希望是一个利用 SIMD 指令的库。例如,Numpy.NET 在 .NET 中公开了 numpy 绑定
  • 考虑提供真实代码而不是伪代码,以便为那些试图提供帮助的人更轻松地启动和运行示例数据。
  • 您说“二维数组”,但您使用的表示法暗示您正在使用“锯齿状数组”。 have different layouts in memory,如果您需要性能关键的优化复制操作,这可能是相关的。
  • 最快的做某事的方法是根本不做...这是否适用于您的情况(即通过将所有 4 个封装到一个以您喜欢的方式公开索引的类中)?
  • @AlexeiLevenkov:我接受了你的想法并付诸实施。见下文。

标签: c#


【解决方案1】:

这是基于 cmets 中Alexei Levenkov 建议的解决方案。从他的评论中:做某事的最快方法是根本不做......这在你的情况下是否有效(即通过将所有 4 个包装到一个以你喜欢的方式公开索引的类)?

您创建了四个单独的数组,然后将它们添加到能够将这些数组保存在矩形数组中的包装器中。我使用一个函数来访问各个元素,但您可以使用索引器来代替(如果您倾向于那样 - 我不是,该访问器对于简单的索引器来说似乎有点过于复杂)。

这是主要的类:

public class CompositeArray<T> where T:new()
{
    private readonly T[,][,] _componentArray = null;

    public int IndividualArrayWidth { get; }
    public int IndividualArrayHeight { get; }
    public int ComponentArrayWidth { get; }
    public int ComponentArrayHeight { get;  }
    public int OverallArrayWidth => IndividualArrayWidth * ComponentArrayWidth;
    public int OverallArrayHeight => IndividualArrayHeight * ComponentArrayHeight;

    public CompositeArray(int individualArrayWidth, int individualArrayHeight, int componentArrayWidth,
        int componentArrayHeight)
    {
        IndividualArrayWidth = individualArrayWidth;
        IndividualArrayHeight = individualArrayHeight;
        ComponentArrayWidth = componentArrayWidth;
        ComponentArrayHeight = componentArrayHeight;
        _componentArray = new T[ComponentArrayWidth, ComponentArrayHeight][,];
    }

    public void SetIndividualArray(int x, int y, T[,] array)
    {
        if (x < 0 || x >= IndividualArrayWidth)
        {
            throw new ArgumentOutOfRangeException(nameof(x), x, $@"Must be between 0 and {IndividualArrayWidth - 1}");
        }
        if (y < 0 || y >= IndividualArrayHeight)
        {
            throw new ArgumentOutOfRangeException(nameof(y), y, $@"Must be between 0 and {IndividualArrayHeight - 1}");
        }
        if (array.GetLength(0) != IndividualArrayWidth || array.GetLength(1) != IndividualArrayHeight)
        {
            throw new ArgumentOutOfRangeException(nameof(array),  $@"Must be between an array that is {IndividualArrayWidth} by {IndividualArrayHeight}");
        }

        _componentArray[x, y] = array;
    }

    public T GetOverallElement(int x, int y)
    {
        if (x < 0 || x >= OverallArrayWidth)
        {
            throw new ArgumentOutOfRangeException(nameof(x), x, $@"Must be between 0 and {OverallArrayWidth - 1}");
        }
        if (y < 0 || y >= OverallArrayHeight)
        {
            throw new ArgumentOutOfRangeException(nameof(y), y, $@"Must be between 0 and {OverallArrayHeight - 1}");
        }

        int whichArrayX = x / IndividualArrayWidth;
        int innerX = x % IndividualArrayWidth;
        int whichArrayY = y / IndividualArrayHeight;
        int innerY = y % IndividualArrayHeight;

        return (_componentArray[whichArrayX, whichArrayY][innerX, innerY]);
    }
}

请注意,我创建了一个矩形锯齿状矩形阵列。花了一点时间弄清楚语法。

请注意,根本没有复制。您只需为每个元素访问支付两次整数除法和两次整数模运算。如果您只使用 2x2 数组,则可以将其减少为两个位移位和两个位测试(因为除以二是一个简单的位移位,并且检查偶数/奇数只是对最低有效位的测试)。

下面是一些练习该类的代码:

 int[,] array00 = new int[,]
 {
     {1, 2, 3},
     {4, 5, 6},
     {7, 8, 9}
 };
 int[,] array01 = new int[,]
 {
     {11, 12, 13},
     {14, 15, 16},
     {17, 18, 19}
 };
 int[,] array10 = new int[,]
 {
     {21, 22, 23},
     {24, 25, 26},
     {27, 28, 29}
 };
 int[,] array11 = new int[,]
 {
     {31, 32, 33},
     {34, 35, 36},
     {37, 38, 39}
 };

 CompositeArray<int> bigArray = new CompositeArray<int>(array00.GetLength(0), array00.GetLength(1), 2,2);
 bigArray.SetIndividualArray(0, 0, array00);
 bigArray.SetIndividualArray(0, 1, array01);
 bigArray.SetIndividualArray(1, 0, array10);
 bigArray.SetIndividualArray(1, 1, array11);

 var shouldBe2 = bigArray.GetOverallElement(0, 1);
 var shouldBe6 = bigArray.GetOverallElement(1, 2);
 var shouldBe28 = bigArray.GetOverallElement(5, 1);
 var shouldBe16 = bigArray.GetOverallElement(1, 5);
 var shouldBe32 = bigArray.GetOverallElement(3, 4);

【讨论】:

    【解决方案2】:

    我认为你能做的最好的就是复制 3 个值的块,因为它们都是在目标数组中连续的源值:

    var ans = new int[6,6];
    
    for (int row = 0; row < 2; ++row) {
        for (int col = 0; col < 2; ++col) {
            var srcArray = chunk_array[row, col];
            for (int subRow = 0; subRow < 3; ++subRow) {
                Array.Copy(srcArray, subRow*3, ans, subRow*6+row*18+col*3, 3);
            }
        }
    }
    

    一些性能测试并未显示此代码的优化程度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-14
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多