[英]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.