【发布时间】:2016-05-31 02:08:02
【问题描述】:
所以我将递归函数转换为迭代函数,然后使用 Parallel.ForEach,但是当我通过 VTune 运行它时,它的大部分运行时间实际上只使用了 2 个逻辑内核。
我决定尝试改用托管线程,并转换了这段代码:
for (int N = 2; N <= length; N <<= 1)
{
int maxThreads = 4;
var workGroup = Enumerable.Range(0, maxThreads);
Parallel.ForEach(workGroup, i =>
{
for (int j = ((i / maxThreads) * length); j < (((i + 1) / maxThreads) * length); j += N)
{
for (int k = 0; k < N / 2; k++)
{
int evenIndex = j + k;
int oddIndex = j + k + (N / 2);
var even = output[evenIndex];
var odd = output[oddIndex];
output[evenIndex] = even + odd * twiddles[k * (length / N)];
output[oddIndex] = even + odd * twiddles[(k + (N / 2)) * (length / N)];
}
}
});
}
进入这个:
for (int N = 2; N <= length; N <<= 1)
{
int maxThreads = 4;
Thread one = new Thread(() => calculateChunk(0, maxThreads, length, N, output));
Thread two = new Thread(() => calculateChunk(1, maxThreads, length, N, output));
Thread three = new Thread(() => calculateChunk(2, maxThreads, length, N, output));
Thread four = new Thread(() => calculateChunk(3, maxThreads, length, N, output));
one.Start();
two.Start();
three.Start();
four.Start();
}
public void calculateChunk(int i, int maxThreads, int length, int N, Complex[] output)
{
for (int j = ((i / maxThreads) * length); j < (((i + 1) / maxThreads) * length); j += N)
{
for (int k = 0; k < N / 2; k++)
{
int evenIndex = j + k;
int oddIndex = j + k + (N / 2);
var even = output[evenIndex];
var odd = output[oddIndex];
output[evenIndex] = even + odd * twiddles[k * (length / N)];
output[oddIndex] = even + odd * twiddles[(k + (N / 2)) * (length / N)];
}
}
}
问题出在 N 循环的最后一次迭代的第四个线程中,我得到一个输出数组的索引越界异常,其中索引尝试访问 length 的等效项。
我无法通过调试来查明原因,但我相信这与线程有关,我在没有线程的情况下运行代码并且它按预期工作。
如果有任何代码需要更改,请告诉我,我通常会有一些人建议修改。感谢您的帮助,我已尝试自己对其进行排序,并且相当确定问题出现在我的线程中,但我看不出是如何发生的。
PS:预期目的是并行化这段代码。
【问题讨论】:
-
Parallel.ForEach 有很多重载,可以通过选项控制并行度。
-
您可以通过将
Parallel.ForEach从原始 sn-p 的内循环移动到外循环来潜在地实现所需的结果(如果您希望它的元素比workGroup的更多课程)。这将减少Parallel.ForEach设置和拆卸成本,并使负载均衡器能够更好地完成工作,我希望它可以扩展到 N 个内核。不过,如果您坚持使用线程,我希望在某处看到Joins - 否则您会在前一批有机会完成之前在每次循环迭代中启动越来越多的线程。 -
你确定你的算法是正确的吗?据我了解,在循环
for (int j = ((i / maxThreads) * length); j < (((i + 1) / maxThreads) * length); j += N)中,初始值int j = ((i / maxThreads) * length在[0,maxThreads-1] 范围内的i 将始终为0(这是整数除法!)。对于除最后一个值之外的所有i值,循环条件`j false。所以最后,无论你使用多少线程,你的内循环只进入一次。 -
没关系
Thread。你刚刚跨过绊线,直接进入了雷区。 TPL 比您认为的要聪明得多——您不必手动将您交给Parallel.ForEach的项目数量限制为您拥有的核心数量。事实上,它永远不会达到这个数字,因为您不允许线程池扩展到您的实际负载。 -
@qbik 你说得对,我在发布后不久就发现了这一点,我已经纠正了这个问题,但又出现了另一个问题,即拆分工作负载是导致索引超出范围的原因。跨度>
标签: c# .net multithreading parallel-processing