我想弄清楚为什么“选择A”表现得更好“选择B”。我的测试显示了228 vs 830或类似的东西,它就像是4倍的差异。看着IL,未经训练的眼睛不会在两次呼叫之间巧妙地挑选出来。
谢谢你, 斯蒂芬
const int SIZE = 10000;
void Main()
{
var sw = Stopwatch.StartNew();
int[,]A = new int[SIZE, SIZE];
int total, x, y;
// Choice A
total = 0;
for (x = 0; x < SIZE; x++)
{
for (y = 0; y < SIZE; y++)
{
total += A[x, y];
}
}
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
// Choice B
total = 0;
for (y = 0; y < SIZE; y++)
{
for (x = 0; x < SIZE; x++)
{
total += A[x, y];
}
}
Console.WriteLine(sw.ElapsedMilliseconds);
}
// Define other methods and classes here
好吧,我把它打破了,以便它们可以彼此独立运行并减轻任何缓存和/或诊断......并且B总是在A背后进入
namespace ConsoleApplication1
{
class ProgramA
{
const int SIZE = 10000;
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
int[,] A = new int[SIZE, SIZE];
int total, x, y;
// Choice A
total = 0;
for (x = 0; x < SIZE; x++)
{
for (y = 0; y < SIZE; y++)
{
total += A[x, y];
}
}
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
class ProgramB
{
const int SIZE = 10000;
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
int[,] A = new int[SIZE, SIZE];
int total, x, y;
// Choice B
total = 0;
for (y = 0; y < SIZE; y++)
{
for (x = 0; x < SIZE; x++)
{
total += A[x, y];
}
}
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
答案 0 :(得分:6)
猜测,缓存效果在这里会很重要。
二维数组如下所示放在内存中:
(0, 0) (0, 1) (0, 2) (0, 3) (1, 0) (1, 1) (1, 2) ...
在选项A中,您正在访问内存中的连续元素 - 这意味着当CPU获取缓存行时,它会获得几个连续的元素。选项B通过内存跳转。因此,一旦数组变得大于缓存大小,选项B就需要更多的内存访问。
答案 1 :(得分:1)
啊,我想我记得了。
如果您将2d数组视为内存中的表,则第一个值是行,第二个值是列。
[0,0] [0,1] [0,2] [0,3] ...... [1,0] [1,1] [1,2] [1,3] ...
当你迭代它时,第一个循环是行,第二个循环是列。通过执行foreach行迭代更快,分配每列。
在第二种情况下,它的值被指定为
[0,0] [1,0] [2,0] [3,0] ...... [0,1] [1,1] [2,1] [3,1] ......
所以这比较慢,因为你的循环,你正在分配foreach列,foreach行。您只为每一行分配第一列。
这有意义吗?
编辑:这是我要找的东西之一:
http://en.wikipedia.org/wiki/Row-major_order
在行主存储中,a 线性多维数组 访问内存使得行是 一个接一个地存储。
因此,当一次迭代一行时,它不会跳过内存寻找下一行来为列分配值,它有行,分配所有列,然后跳转到内存中的下一行。 / p>
答案 2 :(得分:0)
扩展缓存答案:
有问题的值是每个4字节,IIRC当前内存架构从内存中读取16字节行,假设主板已正确填充。 (我不知道DDR3,它的三芯片特性表明读取更大。)因此,当你读取一行内存时,你会得到4个值。
执行此操作时,首先使用所有这些值,然后再返回到下一行的内存。完成第二种方式,你只使用其中一种,然后在它再次被调用之前很久就从片上缓存中刷新。