最快的方式转换成T [,]为T [] []? [英] Fastest way to convert T[,] to T[][]?

查看:119
本文介绍了最快的方式转换成T [,]为T [] []?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原来不会创建所有阵列相同。多维数组可以具有非零下界。例如见的Excel PIA的Range.Value财产对象[,] rectData = myRange.Value;

So it turns out all arrays are not created equal. Multi-dimensional arrays can have non-zero lower bounds. See for example Excel PIA's Range.Value property object[,] rectData = myRange.Value;

我需要这些数据转换成锯齿状排列。下面我第一次尝试味道的复杂性。任何建议,以优化呢?它需要处理的地方下界可能不是零的一般情况。

I need to convert these data into a jagged array. My first try below smells of complexity. Any suggestions to optimize it? It needs to handle the general case where lower bounds may not be zero.

我有这样的前法:

    public static T[][] AsJagged<T>( this T[,] rect )
    {
        int row1 = rect.GetLowerBound(0);
        int rowN = rect.GetUpperBound(0);
        int col1 = rect.GetLowerBound(1);
        int colN = rect.GetUpperBound(1);

        int height = rowN - row1 + 1;
        int width = colN - col1 + 1;
        T[][] jagged = new T[height][];

        int k = 0;
        int l;
        for ( int i = row1; i < row1 + height; i++ )
        {
            l = 0;
            T[] temp = new T[width];
            for ( int j = col1; j < col1 + width; j++ )
                temp[l++] = rect[i, j];
            jagged[k++] = temp;
        }

        return jagged;
    }

用于这样的:

    public void Foo()
    {
        int[,] iRect1 = { { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } };
        int[][] iJagged1 = iRect1.AsJagged();

        int[] lengths = { 3, 5 };
        int[] lowerBounds = { 7, 8 };
        int[,] iRect2 = (int[,])Array.CreateInstance(typeof(int), lengths, lowerBounds);
        int[][] iJagged2 = iRect2.AsJagged();

    }

好奇,如果 Buffer.BlockCopy()将工作或更快?

编辑:AsJagged需要处理引用类型

AsJagged needs to handle reference types.

编辑:在AsJagged发现的bug()。新增 int类型l ;并加入 COL1 +宽度至内循环。

Found bug in AsJagged(). Added int l; and added col1 + width to inner loop.

推荐答案

一个视图警告/假设前面:

A view caveats/assumptions up front:


  • 您好像只使用 INT 作为你的数据类型(或至少似乎是使用 Buffer.BlockCopy 这将意味着你可以生活在一般的基本类型)。

  • 为您展示,我不认为会有任何使用有点理智的方式有很大的区别的测试数据。

  • You seem to use only int as your data type (or at least seem to be OK with using Buffer.BlockCopy which would imply you can life with primitive types in general).
  • For the test data you show, I don't think there will be much different using any somewhat sane approach.

有了这样说,下面的实现(这需要专业化的特定基本类型(这里 INT ),因为它使用了固定)大约比使用内循环的方法快10倍:

Having that said, the following implementation (which needs to be specialized for a specific primitive type (here int) because it uses fixed) is around 10 times faster than the approach using the inner loop:

    unsafe public static int[][] AsJagged2(int[,] rect)
    {
        int row1 = rect.GetLowerBound(0);
        int rowN = rect.GetUpperBound(0);
        int col1 = rect.GetLowerBound(1);
        int colN = rect.GetUpperBound(1);

        int height = rowN - row1 + 1;
        int width = colN - col1 + 1;
        int[][] jagged = new int[height][];

        int k = 0;
        for (int i = row1; i < row1 + height; i++)
        {
            int[] temp = new int[width];

            fixed (int *dest = temp, src = &rect[i, col1])
            {
                MoveMemory(dest, src, rowN * sizeof(int));
            }

            jagged[k++] = temp;
        }

        return jagged;
    }

    [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
    unsafe internal static extern void MoveMemory(void* dest, void* src, int length);

使用下面的测试code:

Using the following "test code":

    static void Main(string[] args)
    {
        Random rand = new Random();
        int[,] data = new int[100,1000];
        for (int i = 0; i < data.GetLength(0); i++)
        {
            for (int j = 0; j < data.GetLength(1); j++)
            {
                data[i, j] = rand.Next(0, 1000);
            }
        }

        Stopwatch sw = Stopwatch.StartNew();

        for (int i = 0; i < 100; i++)
        {
            int[][] dataJagged = AsJagged(data);
        }

        Console.WriteLine("AsJagged:  " + sw.Elapsed);

        sw = Stopwatch.StartNew();

        for (int i = 0; i < 100; i++)
        {
            int[][] dataJagged2 = AsJagged2(data);
        }

        Console.WriteLine("AsJagged2: " + sw.Elapsed);
    }

其中, AsJagged (第一种情况)是你原来的功能,我得到以下的输出:

Where AsJagged (the first case) is your original function, I get the following output:

AsJagged:  00:00:00.9504376
AsJagged2: 00:00:00.0860492

所以,的确有那么这是一种更快的方式,的但是根据测试数据的大小,次数实际执行此操作,你愿意允许不安全和P / Invoke的code,你可能不会需要它

有了这样说,我们使用大型矩阵双(比如7000x10000元素)它的确没有作出了巨大的差异。

Having that said, we were using large matrixes of double (say 7000x10000 elements) where it indeed did make a huge difference.

更新:关于使用Buffer.BlockCopy

我可能会忽略一些元帅或其他伎俩,但使用我不认为 Buffer.BlockCopy 可能这里。这是因为它需要源和目标数组,好了,是这样的事实的阵列

I might overlook some Marshal or other trick, but I don't think using Buffer.BlockCopy is possible here. This is due to the fact that it requires both the source and destination array to, well, be an Array.

在我们的例子中,目标是一个数组(例如 INT [] TEMP = ... ),然而源不是。虽然我们知道基本类型的两维阵列的布局是这样的,即每一行(即第一尺寸)是在存储器的类型的数组,不存在安全(如在不安全)的方式来获得该数组没有先复制它的开销。所以,我们基本上需要使用简单地记忆优惠和不关心它的实际内容的功能 - 如 MoveMemory 。 BTW,内部实行 Buffer.BlockCopy 的做类似的事情。

In our example, the destination is an array (e.g. int[] temp = ...) however the source is not. While we "know" that for two dimensional arrays of primitive types the layout is such, that each "row" (i.e. first dimension) is an array of the type in memory, there is no safe (as in unsafe) way to get that array without the overhead of copying it first. So we basically need to use a function that simply deals with memory and doesn't care about the actual content of it - like MoveMemory. BTW, the internal implementation of Buffer.BlockCopy does something similar.

这篇关于最快的方式转换成T [,]为T [] []?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆