【发布时间】:2010-05-04 02:19:54
【问题描述】:
如何尽快初始化原始类型的多维数组?
我一直坚持使用多维数组。我的问题是性能。以下例程在大约初始化一个 100x100 数组。 500 滴答声。删除 int.MaxValue 初始化会导致大约。仅用于循环的 180 个滴答声。大约 100 个滴答来创建数组,无需循环且无需初始化为 int.MaxValue。
- 在“运行”期间,与此类似的例程被调用几十万到几百万次。
- 数组大小在运行期间不会改变,数组是一次创建一个,使用,然后丢弃,并创建一个新数组。
- “运行”可能会持续一分钟(使用 10x10 阵列)到四十五分钟 (100x100)。
- 应用程序创建 int、bool 和 struct 数组。
- 可以同时执行多个“运行”,但不是因为性能严重下降。
- 我使用 100x100 作为基线。
我愿意接受有关如何优化数组的这种非默认初始化的建议。我的一个想法是在可用时使用较小的原始类型。例如,使用 byte 而不是 int,可以节省 100 个刻度。我会对此感到满意,但我希望不必更改原始数据类型。
public int[,] CreateArray(Size size) {
int[,] array = new int[size.Width, size.Height];
for (int x = 0; x < size.Width; x++) {
for (int y = 0; y < size.Height; y++) {
array[x, y] = int.MaxValue;
}
}
return array;
}
以下是 450 个滴答声:
public int[,] CreateArray1(Size size) {
int iX = size.Width;
int iY = size.Height;
int[,] array = new int[iX, iY];
for (int x = 0; x < iX; x++) {
for (int y = 0; y < iY; y++) {
array[x, y] = int.MaxValue;
}
}
return array;
}
创建数组5;接受的实施:限制:无法调整大小,可以更改
在一次性初始化 2800 个刻度后,减少到大约 165 个刻度。 (请参阅下面的答案。)如果我可以让stackalloc 处理多维数组,我应该能够获得相同的性能而无需初始化private static 数组。
private static bool _arrayInitialized5;
private static int[,] _array5;
public static int[,] CreateArray5(Size size) {
if (!_arrayInitialized5) {
int iX = size.Width;
int iY = size.Height;
_array5 = new int[iX, iY];
for (int x = 0; x < iX; x++) {
for (int y = 0; y < iY; y++) {
_array5[x, y] = int.MaxValue;
}
}
_arrayInitialized5 = true;
}
return (int[,])_array5.Clone();
}
创建数组8;接受的实施;限制:需要完全信任
在不使用上面的“克隆技术”的情况下,可以减少到大约 165 个滴答声。 (请参阅下面的答案。)如果我能弄清楚CreateArray9 的返回值,我相信我可以降低滴答声。
public unsafe static int[,] CreateArray8(Size size) {
int iX = size.Width;
int iY = size.Height;
int[,] array = new int[iX, iY];
fixed (int* pfixed = array) {
int count = array.Length;
for (int* p = pfixed; count-- > 0; p++)
*p = int.MaxValue;
}
return array;
}
注意事项
我将提供有关此问题的所有代码和注释作为答案。希望它会在未来节省一些人的时间。
在大对象堆 (LOH) 上分配的数组不在此讨论范围内。提到的性能改进仅适用于在堆上分配的数组。
堆栈分配
我使用stackalloc 来消除将数组初始化为默认值的想法没有成功,因为分配的堆栈内存必须从方法中复制出来。意思是,我必须创建另一个数组来保存结果。该数组将被初始化,从而破坏了使用 stackalloc 的全部目的。
创建数组8;不安全/固定的方法
如果 unsafe 代码位于完全受信任的程序集中,CLR 只会执行它。
创建数组5;克隆方法
需要变量来确定数组是否已初始化并存储已初始化的数组。性能与初始化后的 unsafe/fixed 方法相同。有关可能的解决方案,请参阅 Dan Tao 的答案。
300% 性能提升?
百分比我很烂,但我认为是 300%(500 到 165 个滴答声)。
应用的最终实现
对于这个应用程序,我决定使用“克隆”方法。以下是应用程序中使用的“精益”通用实现以及性能示例。
初始化:
-
Grid<int>;通用克隆类初始化:4348、4336、4339、4654 -
Grid<bool>;通用克隆类初始化:2692、2684、3916、2680 -
Grid<Color>;通用克隆类初始化:3747、4630、2702、2708
用途:
-
Grid<int>;通用克隆类:185、159、152、290 -
Grid<bool>;通用克隆类:39、36、44、46 -
Grid<Color>;通用克隆类:2229、2431、2460、2496public class Grid<T> { private T[,] _array; private T _value; private bool _initialized; private int _x; private int _y; public Grid(Size size, T value, bool initialize) { _x = size.Width; _y = size.Height; _value = value; if (initialize) { InitializeArray(); } } private void InitializeArray() { int iX = _x; int iY = _y; _array = new T[iX, iY]; for (int y = 0; y < iY; y++) { for (int x = 0; x < iX; x++) { _array[x, y] = _value; } } _initialized = true; } public T[,] CreateArray() { if (!_initialized) { InitializeArray(); } return (T[,])_array.Clone(); } }
【问题讨论】:
-
愚蠢的问题:为什么需要将每个槽初始化为
int.MaxValue? -
@丹涛; -1 表示愚蠢的评论。
-
@AMissico:哈,等等,我想你误会了。我的意思是我的问题是愚蠢的,而不是你的!
-
@丹涛; +1 为良好的复出。 :O)
-
未记录的数组特征;这里有一些有趣的优化,比如使用锯齿状而不是矩形数组来减少内部 CLR 检查 - codeproject.com/KB/dotnet/arrays.aspx
标签: .net initialization multidimensional-array