【问题标题】:convert 3 dimensional array to a 2 dimensional array将 3 维数组转换为 2 维数组
【发布时间】:2014-11-02 18:20:28
【问题描述】:

我有这个字节数组:

byte[,,] somedata = new byte[100,150,3];
~somedata is now populated by an external input.

我只想使用以下值:

byte[,] useThisDataOnly = new byte[100,150];

目前,我使用这个:

foreach (int x=0,x< 100; x++)
{
    foreach (int y=0,y< 150; y++)
    {
       useThisDataOnly [x,y] = somedata[x,y,0];
    }
}

有没有更快的方法来做到这一点?

另外,我将把这个 useThisDataOnly 扁平化(因为我听说它操作起来更快)。

有什么建议吗?

【问题讨论】:

  • 更快、更高性能是什么意思?在这些数组大小下,除非您经常这样做,否则无关紧要。
  • @thumbmunkeys 嗨,感谢您的时间和评论,是的,我被要求每秒接收许多这样的数组(呻吟 - 我无法控制)。我想我在这里问了两个问题。我的主要重点是将 3dim 转换为 2dim,而无需在低内存使用情况下使用 foreach 并且尽可能快:)
  • 嗨@AlexFarber 一个很好的链接(我+1)但示例显示[,]到[]。我理想地追求的是 [,,] 到 []?
  • 我建议不要预先优化内存,而是先尝试快速和脏循环,然后测量性能。然后你就会知道如果这是个问题。如果是这样,请查看并行性;请参阅任务并行库
  • 您总是可以拥有一个 C++/CLI 库(允许您编写真正的非托管代码并将其反馈给普通的 C# 应用程序)来完成密集的方面。但我的猜测是,如果您可以将工作分成 4/8 部分并执行并行任务,您将获得所需的性能。

标签: c# arrays flatten


【解决方案1】:

优化这一点的困难在于,您想要遗漏的字节位于最“快速移动”的索引中。

我的意思是:如果你像这样为每个数组元素分配连续的数字:

byte[,,] source = new byte[100, 150, 3];

for (int i = 0, n = 0; i < 100; ++i)
    for (int j = 0; j < 150; ++j)
        for (int k = 0; k < 3; ++k, ++n)
            source[i, j, k] = unchecked ((byte)n);

如果你以扁平化的顺序打印出内容,你会得到 0..255 重复。

然后你排除最终维度:

for (int i = 0; i < 100; ++i)
    for (int j = 0; j < 150; ++j)
        dest[i, j] = source[i, j, 0];

如果您以展平顺序打印结果,您将获得:

0, 3, 6, 9, 12, 15, ...

因此您可以看到,为了将输入转换为输出,您需要每三个字节获取一次,不幸的是,没有快速的方法可以做到这一点。

但是,如果您能够要求创建数组并首先省略索引:

byte[,,] source = new byte[3, 100, 150];

那么您能够使用Buffer.BlockCopy()

我想这不是一个选项 - 如果没有,手动编码循环可能是您在不使用不安全代码的情况下能够做到的最快的循环。

不安全的代码

以下是使用不安全代码和指针的方法。这甚至可能不会让它更快,所以在选择这样做之前,您需要 (a) 确保执行简单的方法真的太慢并且 (b) 使用 Stopwatch 对发布构建进行一些仔细的计时以确保确实值得使用不安全的代码。

还要注意使用不安全代码的缺点!不安全的代码需要提升权限,并可能导致您的程序因低级 C++ 风格的野指针错误而崩溃!

using System;
using System.Diagnostics;

namespace Demo
{
    internal static class Program
    {
        static void Main()
        {
            byte[,,] source = new byte[100, 150, 3];

            for (int i = 0, n = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    for (int k = 0; k < 3; ++k, ++n)
                        source[i, j, k] = unchecked ((byte)n);

            byte[,] dest1 = new byte[100, 150];
            byte[,] dest2 = new byte[100, 150];

            for (int i = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    dest1[i, j] = source[i, j, 0];

            unsafe
            {
                fixed (byte* sp = source)
                fixed (byte* dp = dest2)
                {
                    byte* q = dp;
                    byte* p = sp;

                    for (int i = 0; i < 100*150; ++i)
                    {
                        *q++ = *p;
                        p += 3;
                    }
                }
            }

            for (int i = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    Trace.Assert(dest1[i, j] == dest2[i, j], "Arrays should be identical");
        }
    }
}

我个人认为无论如何使用简单、安全的循环不会太慢,但现在至少你有一些代码可以尝试。

【讨论】:

  • 您好,感谢您提供的如此全面的信息。我明白你在说什么,这对我来说很有意义。不幸的是,我无法控制输入。但我对不安全的代码声明感到好奇。您是否暗示有一种使用不安全技术的方法,如果是这样,您是否建议不要这样做?谢谢你的时间..
  • @AndrewSimpson 是的,您可以使用不安全的代码和指针,但这意味着您的程序集(或使用它的任何东西)必须标记为“不安全”,这可能会产生安全隐患。不过,我会发布示例代码,以便您查看。它可能会使其更快一些 - 但很可能不会!
  • 你真是太好了。非常感谢您的所有时间!
  • 嗨,非常感谢。我会按照您的建议尝试并测试。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 2021-09-18
  • 2016-12-30
  • 2023-03-26
  • 2019-06-29
  • 2016-02-18
相关资源
最近更新 更多