【发布时间】:2015-07-01 02:05:59
【问题描述】:
最近,我回答了一个关于优化可能的并行化方法以生成任意基数的每个排列的问题。我发布了一个类似于 Parallelized, bad implementation 代码块列表的答案,几乎立即有人指出了这一点:
这几乎可以保证给您虚假共享,并且可能会慢很多倍。 (感谢gjvdkamp)
他们是对的,这是死亡缓慢。也就是说,我研究了该主题,并找到了一些 interesting material and suggestions(仅限 MSDN 杂志存档,.NET Matters: False Sharing)来对抗它。如果我理解正确,当线程访问连续内存(比如说,可能支持 ConcurrentStack 的数组)时,可能会发生错误共享。
对于水平线以下的代码,Bytes 是:
struct Bytes {
public byte A; public byte B; public byte C; public byte D;
public byte E; public byte F; public byte G; public byte H;
}
对于我自己的测试,我想获得一个并行版本的运行并真正更快,所以我基于原始代码创建了一个简单的示例。 6 as limits[0] 对我来说是一个懒惰的选择——我的电脑有 6 个内核。
单线程阻塞 平均运行时间:10s0059ms
var data = new List<Bytes>();
var limits = new byte[] { 6, 16, 16, 16, 32, 8, 8, 8 };
for (byte a = 0; a < limits[0]; a++)
for (byte b = 0; b < limits[1]; b++)
for (byte c = 0; c < limits[2]; c++)
for (byte d = 0; d < limits[3]; d++)
for (byte e = 0; e < limits[4]; e++)
for (byte f = 0; f < limits[5]; f++)
for (byte g = 0; g < limits[6]; g++)
for (byte h = 0; h < limits[7]; h++)
data.Add(new Bytes {
A = a, B = b, C = c, D = d,
E = e, F = f, G = g, H = h
});
并行化,实施不佳 平均运行时间:81s729ms,~ 8700 次争用
var data = new ConcurrentStack<Bytes>();
var limits = new byte[] { 6, 16, 16, 16, 32, 8, 8, 8 };
Parallel.For(0, limits[0], (a) => {
for (byte b = 0; b < limits[1]; b++)
for (byte c = 0; c < limits[2]; c++)
for (byte d = 0; d < limits[3]; d++)
for (byte e = 0; e < limits[4]; e++)
for (byte f = 0; f < limits[5]; f++)
for (byte g = 0; g < limits[6]; g++)
for (byte h = 0; h < limits[7]; h++)
data.Push(new Bytes {
A = (byte)a,B = b,C = c,D = d,
E = e,F = f,G = g,H = h
});
});
并行化,??实施 平均运行时间:5s833ms,92 次争用
var data = new ConcurrentStack<List<Bytes>>();
var limits = new byte[] { 6, 16, 16, 16, 32, 8, 8, 8 };
Parallel.For (0, limits[0], () => new List<Bytes>(),
(a, loop, localList) => {
for (byte b = 0; b < limits[1]; b++)
for (byte c = 0; c < limits[2]; c++)
for (byte d = 0; d < limits[3]; d++)
for (byte e = 0; e < limits[4]; e++)
for (byte f = 0; f < limits[5]; f++)
for (byte g = 0; g < limits[6]; g++)
for (byte h = 0; h < limits[7]; h++)
localList.Add(new Bytes {
A = (byte)a, B = b, C = c, D = d,
E = e, F = f, G = g, H = h
});
return localList;
}, x => {
data.Push(x);
});
我很高兴我有一个比单线程版本更快的实现。我预计结果接近 10s / 6 或 1.6 秒左右,但这可能是一个幼稚的期望。
我的问题是对于实际上比单线程版本更快的并行化实现,是否有可以应用于操作的进一步优化?我想知道与并行化相关的优化,而不是改进用于计算值的算法。具体来说:
- 我知道存储和填充为
struct而不是byte[]的优化,但它与并行化无关(或者是吗?) - 我知道可以使用波纹进位加法器对所需值进行惰性求值,但与
struct优化相同。
【问题讨论】:
-
你最好把这个发到programmers 吗?更好的是让 1 成为高尔夫 challenge
-
@lloydm 在stackoverflow中有这个问题有什么问题?很高兴这里至少有一些有趣的、具有挑战性的问题,而不仅仅是一百万条错误消息或语法问题
-
@Prokurors 毫无疑问,它既有趣又具有挑战性。我已经了解了虚假分享。再次阅读有效问题后,我同意它在方框中打勾作为有效问题。
-
反对者,我该如何改进我的问题?
-
您的实现也不是 List 的最佳选择。您确切知道列表中需要多少个元素,因此您可以在构造函数中设置容量并防止不必要的分配。
标签: c# performance parallel-processing false-sharing