我有一个高度并行化的问题。同一个函数需要解决数百个不同的问题。每个问题在单核上平均花费120毫秒(0.12秒)的时间,但也有很大的差异,一些极端和罕见的问题可能花费10倍的时间。每个问题都需要内存,但内存是提前分配的。这些问题不需要磁盘I/O,而且它们在运行时不会来回传递任何变量。但是,它们可以访问同一个全局结构体的不同部分(数组元素)。
我有c++代码,基于别人的代码,工作。(没有显示结构体的全局数组。)它运行20个问题(例如),然后返回。我认为20足以平衡4核的可变性。我看到执行时间已经从大约10开始逐渐变平了。
有一个Win32和一个OpenMP版本,它们在执行时间方面的行为几乎相同。我在四核Windows系统上运行这个程序。我在下面包含了一些OpenMP代码,因为它更短。(我更改了名称等,使其更通用,我可能犯了错误-它不会独立编译)
与单线程版本相比,速度提升在2.3倍左右趋于平缓。所以如果单线程需要230秒,那么多线程需要100秒。我很惊讶,加速并没有接近4,内核的数量。
我应该失望吗?
我能做些什么来接近我的理论期望吗?
int split_bigtask(Inputs * inputs, Outputs * results)
{
for (int k = 0; k < MAXNO; k++)
results->solved[k].value = 0;
int res;
#pragma omp parallel shared(inputs, outputs)
{
#pragma omp for schedule(dynamic)
for (int k = 0; k < inputs->no; k++)
{
res = bigtask(inputs->values[k],
outputs->solved[k],
omp_get_thread_num()
);
}
}
return TRUE;
}
- 我假设在
bigtask()
内没有同步完成(很明显,但我仍然会先检查它)。 - 这是可能的,你遇到了一个"脏缓存"的问题:如果你操作的数据是彼此接近(例如相同的缓存线!)从多个核心,每个操作将标记缓存线为脏(这意味着处理器需要信号这到所有其他处理器,这又涉及到同步…)。 你创建了太多的线程(分配一个线程是相当大的开销)。因此,为每个核心创建一个线程比为每个核心创建5个线程要高效得多。
我个人认为你有情况2 ("Big Global Array")。
问题的解决方案(如果确实是情况2):
- 将结果写入本地数组,该数组在工作结束后由主线程合并为"大全局数组"
- 将全局数组拆分为几个较小的数组(并为每个线程分配其中一个数组)
- 确保结构中的记录在cache - line边界上对齐(这有点hack,因为缓存线边界可能会在未来的处理器中改变)您可能想尝试为每个线程创建数组的本地副本(至少对于结果)