简体   繁体   English

3D 数组和 2D 对象数组之间的 C# 性能

[英]C# Performance between a 3D array and a 2D array of objects

I'm working on a project where I open and manipulate an image file.我正在开发一个打开和操作图像文件的项目。 Until now I was using the Bitmap class but I recently decided to instead make my own class for the image.到目前为止,我一直在使用 Bitmap 类,但我最近决定为图像创建自己的类。 I want to have two dimensions, X and Y, for fetching the pixels, but rather than each pixel being a Color I want another dimension where I can encode additional data.我想要两个维度,X 和 Y,用于获取像素,但不是每个像素都是颜色,而是我想要另一个维度,我可以在其中编码额外的数据。 For example, I want an additional int that can flag other methods to ignore a particular pixel.例如,我想要一个额外的 int 可以标记其他方法以忽略特定像素。

My question is, should I make a pixel object and add fields to it, then make a 2D array of pixels?我的问题是,我应该制作一个像素对象并向其添加字段,然后制作一个二维像素数组吗? Or should I just make a 3D array?还是我应该只制作一个 3D 数组?

The image will probably be up to 3000 pixels wide/high and I'll probably want up to 10 fields.图像可能高达 3000 像素宽/高,我可能需要多达 10 个字段。 So what would be the more efficient option?那么更有效的选择是什么? If it's negligible I would prefer the ease of creating an object.如果它可以忽略不计,我更喜欢创建对象的便利性。

It has been demonstrated that 2D arrays are a lot slower than 1D arrays in C#.已经证明 2D 数组比 C# 中的 1D 数组慢很多。

Typically you would pack a 2D array into a long 1D array, laying it down row by row.通常,您会将 2D 数组打包成一个长的 1D 数组,逐行放置。 You use this as a 2D array but internally is a 1D array.您将其用作 2D 数组,但在内部是 1D 数组。

static class Program
{
    static void Main(string[] args)
    {
        var image = new Matrix<MyColor>(3000, 3000);

        MyColor pixel = image[374, 991];
        image[867, 1723] = new MyColor(Color.Chartreuse, 1001, -1);
    }
}

In your case, the element of the array can be to your own struct with the fields you want.在您的情况下,数组的元素可以是您自己的结构,其中包含您想要的字段。

public readonly struct MyColor
{
    public MyColor(Color color, int x1, int x2)
    {
        this.Color = color;
        this.Value1 = x1;
        this.Value2 = x2
    }
    public Color Color { get; }
    public int Value1 { get; }
    public int Value2 { get; }
}

the basic idea for coding a packed array matrix follows:编码压缩数组矩阵的基本思想如下:

public class Matrix<T> : ICollection<T>, System.Collections.ICollection
{
    readonly T[] data;

    public Matrix(int rows, int columns)
    {
        this.data = new T[rows * columns];
        this.Rows = rows;
        this.Columns = columns;
    }
    public Matrix(T[] data, int rows, int columns)
    {
        this.data = data;
        this.Rows = rows;
        this.Columns = columns;
    }

    public int Rows { get; }
    public int Columns { get; }

    public ref T this[int row, int column]
        => ref data[row*Columns+column];

    // other code here

    public static implicit operator T[,](Matrix<T> matrix)
        => matrix.data.Unpack(matrix.Rows, matrix.Columns);
}

and in the end, if you want to convert a 1D array into a 2D array and back you can do it fast using the following extensions methods/utilities.最后,如果您想将一维数组转换为二维数组并返回,您可以使用以下扩展方法/实用程序快速完成。

public static class ArrayEx
{
    /// <summary>
    /// Create a copy of an array with new size.
    /// </summary>
    /// <param name="array">The input array.</param>
    /// <param name="length">The desired length.</param>
    public static T[] Resize<T>(this T[] array, int length)
    {
        T[] result = new T[length];
        var byteCount = Math.Min(Buffer.ByteLength(array), Buffer.ByteLength(result));
        Buffer.BlockCopy(array, 0, result, 0, byteCount);
        return result;
    }

    /// <summary>
    /// Create a copy of a matrix with new size.
    /// </summary>
    /// <param name="matrix">The input matrix.</param>
    /// <param name="rows">The desired rows.</param>
    /// <param name="columns">The desired columns.</param>
    public static T[,] Resize<T>(this T[,] matrix, int rows, int columns)
    {
        T[,] result = new T[rows, columns];
        var byteCount = Math.Min(Buffer.ByteLength(matrix), Buffer.ByteLength(result));
        Buffer.BlockCopy(matrix, 0, result, 0, byteCount);
        return result;
    }

    /// <summary>
    /// Pack a 2D matrix into an array.
    /// </summary>
    /// <param name="matrix">The input matrix.</param>
    /// <param name="length">The desired length or zero for best fit.</param>
    public static T[] Pack<T>(this T[,] matrix, int length = 0)
    {
        length = Math.Max(matrix.Length, length);
        T[] result = new T[length];
        var byteCount = Math.Min(Buffer.ByteLength(matrix), Buffer.ByteLength(result));
        Buffer.BlockCopy(matrix, 0, result, 0, byteCount);
        return result;
    }

    /// <summary>
    /// Unpack an array into a 2D matrix
    /// </summary>
    /// <param name="array">The input array.</param>
    /// <param name="rows">The number rows requested.</param>
    /// <param name="columns">The number of columns requested, 
    /// or zero for best fit.</param>
    public static T[,] Unpack<T>(this T[] array, int rows, int columns = 0)
    {
        if (columns == 0)
        {
            columns = (int)Math.Ceiling(array.Length / (1f * rows));
        }
        int length = Math.Max(array.Length, rows * columns);
        T[,] result = new T[rows, columns];
        var byteCount = Math.Min(Buffer.ByteLength(array), Buffer.ByteLength(result));
        Buffer.BlockCopy(array, 0, result, 0, byteCount);
        return result;
    }
}

Edit 1编辑 1

I think Buffer.BlockCopy() only works for primitives types.我认为Buffer.BlockCopy()仅适用于原始类型。 For user types, there are limited options to do a fast memory copy from a 2D array to a 1D and back.对于用户类型,从 2D 数组到 1D 并返回的快速内存复制的选项有限。 Internally the CLR stores 2D arrays row by row, as a 1D array, but there is no easy way to expose the internal data storage. CLR 在内部逐行存储二维数组,作为一维数组,但是没有简单的方法来公开内部数据存储。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM