我用旅行推销员的SSE指令实现了xmmintrin.h的一个版本,得到了一个不错的加速。但是现在我也在尝试在其之上实现OpenMP线程,并且我看到了相当大的速度减慢。在这两种情况下,我都得到了正确的答案(即(i)只使用SSE,或者(ii)使用SSE && OpenMP)。
我知道我可能做错了什么,也许比我更有经验的人能发现问题。
我的程序的主循环有以下(简短)伪码:
int currentNode;
for(int i = 0; i < numNodes; i++) {
minimumDistance = DBL_MAX;
minimumDistanceNode;
for(int j = 0; j < numNodes; j++) {
// find distance between 'currentNode' to j-th node
// ...
if(jthNodeDistance < minimumDistance) {
minimumDistance = jthNodeDistance;
minimumDistanceNode = jthNode;
}
}
currentNode = minimumDistanceNode;
}下面是我的实现,,它仍然是半伪代码,因为我仍然浏览了一些我认为对性能没有影响的部分,我认为我的代码中的问题可以在下面的代码片段中找到。如果您只是省略了#pragma行,那么下面的内容与仅使用SSE版本的同一程序几乎完全相同,因此我想我应该只包括OpenMP版本:
int currentNode = 0;
#pragma omp parallel
{
#pragma omp single
{
for (int i = 1; i < totalNum; i++) {
miniumum = DBL_MAX;
__m128 currentNodeX = _mm_set1_ps(xCoordinates[currentNode]);
__m128 currentNodeY = _mm_set1_ps(yCoordinates[currentNode]);
#pragma omp parallel num_threads(omp_get_max_threads())
{
float localMinimum = DBL_MAX;
float localMinimumNode;
#pragma omp for
for (int j = 0; j < loopEnd; j += 4) {
// a number of SSE vector calculations to find distance
// between the current node and the four nodes we're looking
// at in this iteration of the loop:
__m128 subXs_0 = _mm_sub_ps(currentNodeX, _mm_load_ps(&xCoordinates[j]));
__m128 squareSubXs_0 = _mm_mul_ps(subXs_0, subXs_0);
__m128 subYs_0 = _mm_sub_ps(currentNodeY, _mm_load_ps(&yCoordinates[j]));
__m128 squareSubYs_0 = _mm_mul_ps(subYs_0, subYs_0);
__m128 addXY_0 = _mm_add_ps(squareSubXs_0, squareSubYs_0);
float temp[unroll];
_mm_store_ps(&temp[0], addXY_0);
// skipping stuff here that is about getting the minimum distance and
// it's equivalent node, don't think it's massively relevant but
// each thread will have its own
// localMinimum
// localMinimumNode
}
// updating the global minimumNode in a thread-safe way
#pragma omp critical (update_minimum)
{
if (localMinimum < minimum) {
minimum = localMinimum;
minimumNode = localMinimumNode;
}
}
}
// within the 'omp single'
ThisPt = minimumNode;
}
}
}所以我的逻辑是:
omp single用于顶级for(int ) for循环,并且只需要一个专门用于此的线程。omp parallel num_threads(omp_get_max_threads())用于内部for(int ) for -循环,因为我希望所有内核同时处理这部分代码。omp critical位于full for(int )循环的末尾,因为我想执行线程--安全地更新当前节点。就运行时而言,OpenMP版本通常是纯SSE版本的两倍。
在我的代码中,有什么东西是特别糟糕的吗?这会导致OpenMP的速度急剧下降吗?
发布于 2021-04-06 06:58:20
在我的代码中,有什么东西是特别糟糕的吗?这会导致OpenMP的速度急剧下降吗?
第一:
(Int i) for循环的顶层omp单线程,我只想要一个专用于此的线程。
在您的代码中,您有以下内容:
#pragma omp parallel
{
#pragma omp single
{
for (int i = 1; i < totalNum; i++)
{
#pragma omp parallel num_threads(omp_get_max_threads())
{
//....
}
// within the 'omp single'
ThisPt = minimumNode;
}
}
}#pragma omp parallel创建了一个线程团队,但是只有一个线程执行并行任务(即#pragma omp single),而其他线程不执行任何操作。您可以简化为:
for (int i = 1; i < totalNum; i++)
{
#pragma omp parallel num_threads(omp_get_max_threads())
{
//....
}
ThisPt = minimumNode;
}内部仅由一个线程执行。
第二:
omp并行num_threads(omp_get_max_threads())用于内部for(int ) for -循环,因为我希望所有内核同时处理这部分代码。
问题是,这可能会返回逻辑核的数目,而不是物理核,而且一些代码在超线程处理时的性能可能会更差。因此,我首先用不同数量的线程进行测试,从2、4等开始,直到找到代码停止缩放的数字为止。
omp关键在full for(int j)循环的末尾,因为我想线程-安全地更新当前节点。
// updating the global minimumNode in a thread-safe way
#pragma omp critical (update_minimum)
{
if (localMinimum < minimum) {
minimum = localMinimum;
minimumNode = localMinimumNode;
}
}这可以通过创建一个数组来替代,其中每个线程将其局部最小值保存在保留给该线程的位置,并且在并行区域外,初始线程提取minimum和minimumNode。
int total_threads = /..;
float localMinimum[total_threads] = {DBL_MAX};
float localMinimumNode[total_threads] = {DBL_MAX};
#pragma omp parallel num_threads(total_threads)
{
/...
}
for(int i = 0; i < total_threads; i++){
if (localMinimum[i] < minimum) {
minimum = localMinimum[i];
minimumNode = localMinimumNode[i];
}
}最后,在完成这些更改之后,您将尝试检查是否可以通过以下方法替换此并行化:
#pragma omp parallel for
for (int i = 1; i < totalNum; i++)
{
...
}https://stackoverflow.com/questions/66961529
复制相似问题