【发布时间】:2013-04-15 15:33:45
【问题描述】:
我有一些奇怪的性能结果,我无法完全解释。 好像这条线
d = new double[4, 4]{{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},};
比这个慢 4 倍
d = new double[4, 4];
d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0;
d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0;
d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0;
d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1;
(甚至没有考虑到在这个例子中我可以省略所有那些= 0 分配)
我知道由于边界检查,在 c# 中循环多维数组可能会很慢。但是这里没有循环,不需要边界检查,整个数组初始化行可以在编译时解析。
然而,第二个代码块必须首先将数组初始化为零,然后单独覆盖每个值。
那么这里的问题是什么?
如果性能是一个问题,那么初始化这个数组的最佳方法是什么?
我使用以下代码来衡量性能:
using System;
using System.Diagnostics;
class Program
{
public static double[,] d; // global static variable to prevent the JIT optimizing it away
static void Main(string[] args)
{
Stopwatch watch;
int numIter = 10000000; // repeat all tests this often
double[,] d2 = new double[4, 4]{{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},};
// ================================================================
// use arrayInitializer: slowest
watch = Stopwatch.StartNew();
for (int i = 0; i < numIter; i++)
{
d = new double[4, 4]{{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},};
}
Console.WriteLine("ArrayInitializer: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);
// ================================================================
// use Array.Copy: faster
watch = Stopwatch.StartNew();
for (int i = 0; i < numIter; i++)
{
d = new double[4, 4];
Array.Copy(d2, d, d2.Length);
}
Console.WriteLine("new + Array.Copy: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);
// ================================================================
// direct assignment: fastest
watch = Stopwatch.StartNew();
for (int i = 0; i < numIter; i++)
{
d = new double[4, 4];
d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0;
d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0;
d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0;
d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1;
}
Console.WriteLine("direct assignment: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);
}
}
结果:
ArrayInitializer: 0,0007917ms
new + Array.Copy: 0,0002739ms
direct assignment: 0,0002281ms
【问题讨论】:
-
看看编译后的 IL 代码是非常不同的。 ArrayInitializer 使用方法 RuntimeHelpers.InitializeArray。但这是我能做的最好的了……有趣的问题!
-
你从不使用创建的数组,那么整个数组分配不会被编译器优化掉吗?
-
这就是我将数组设为公共静态的原因。如果它只是一个局部变量,它确实被优化掉了,但仅适用于带有数组初始化程序的第一个测试用例。但是如果
d是一个静态变量,就不应该进行这样的优化,因为可以想象另一个线程可以访问它;时间测试似乎证实了这一点。 -
为了比较,在 OSX 上使用单声道运行时,我得到
ArrayInitializer: 0.0006183ms new + Array.Copy: 0.0053461ms direct assignment: 0.0005756ms -
你在编译发布模式吗?
标签: c# arrays performance .net-4.0 multidimensional-array