得到了一些简单的代码
Int32[] tmpInt = new Int32[32];
long lStart = DateTime.Now.Ticks;
Thread t1 = new Thread(new ThreadStart(delegate()
{
for (Int32 i = 0; i < 100000000; i++)
Interlocked.Increment(ref tmpInt[5]);
}));
Thread t2 = new Thread(new ThreadStart(delegate()
{
for (Int32 i = 0; i < 100000000; i++)
Interlocked.Increment(ref tmpInt[20]);
}));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine(((DateTime.Now.Ticks - lStart)/10000).ToString());
这在我的 core 2 duo 上大约需要 3 秒。如果我将 t1 中的索引更改为 tmpInt[4],则需要约 5.5 秒。
无论如何,第一个缓存行在索引 4 处结束。因为一个缓存行有 64 个字节,而 5 个 int32 只有 20 个字节,这意味着在实际数组之前有 44 个字节的元数据和/或填充。
我测试的另一组值,其中 5 和 21。5 和 21 需要大约 3 秒,但是 5 和 20 需要大约 5.5 秒,但这是因为索引 20 与索引 5 共享相同的缓存行,因为它们是间隔开的在相同的 64 字节内。
所以我的问题是,.Net 在数组之前保留了多少数据,这个数量在 32 位和 64 位系统之间是否有变化?
谢谢:-)
最佳答案
当 CPU 尝试加载您的数组并遇到缓存未命中时,它会获取包含您的数组的内存块,但不一定从它开始。 .NET 不保证您的数组将缓存对齐。
为了回答您的问题,44 字节的填充大部分是来自关联页面的其他数据,这些数据恰好位于同一缓存行中。
编辑:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx似乎表明一个数组有 16 个字节的额外存储空间。 4 个字节是同步块(synchronized block)索引,4 个字节用于类型句柄元数据,其余是对象本身。
作为附带评论,很难准确地说虚假共享是您在这里延迟的原因。很可能给定了时间,但您应该使用一个好的分析器来检查缓存未命中率。如果它在您的给定案例中跳得很高,您可以非常确定您看到了虚假分享。
关于c# - 数组元数据问题(缓存行),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4403278/