【问题标题】:OpenMP C program run slower than sequential codeOpenMP C 程序运行速度比顺序代码慢
【发布时间】:2019-04-23 22:26:31
【问题描述】:

我是 OpenMP 的新手,正在尝试并行化 Jarvis 的算法。然而事实证明,与顺序代码相比,并行程序花费的时间要长 2-3 倍。

是不是问题本身不能并行化?或者我并行化它的方式有问题。

这是我解决这个问题的 openMP 程序,有 2 个部分被并行化:

#include <stdio.h>
#include <sys/time.h>
#include <omp.h>

typedef struct Point
{
int x, y;
} Point;

// To find orientation of ordered triplet (p, q, r).
// The function returns
// 0 for colinear points
// 1 as Clockwise
// 2 as Counterclockwise
int orientation(Point p, Point i, Point q)
{
int val = (i.y - p.y) * (q.x - i.x) -
          (i.x - p.x) * (q.y - i.y);
if (val == 0) return 0;  // colinear
return (val > 0)? 1: 2; // clock or counterclock wise
}

// Prints convex hull of a set of n points.
void convexHull(Point points[], int n)
{
// There must be at least 3 points
if (n < 3) return;

// Initialize array to store results
Point results[n];
int count = 0;

// Find the leftmost point
int l = 0,i;

#pragma omg parallel shared (n,l) private (i)
{
    #pragma omp for
    for (i = 1; i < n; i++)
    {
        #pragma omp critical
        {
            if (points[i].x < points[l].x)
            l = i;
        }
    }

}

// Start from leftmost point, keep moving counterclockwise
// until reach the start point again.
int p = l, q;
do
{
    // Add current point to result
    results[count]= points[p];
    count++;

    q = (p+1)%n;
    int k;

    #pragma omp parallel shared (p) private (k)
    {
        #pragma omp for 
        for (k = 0; k < n; k++)
        {
           // If i is more counterclockwise than current q, then
           // update i as new q
           #pragma omp critical
           {
            if (orientation(points[p], points[k], points[q]) == 2)
               q = k;
           }
        }       

    }

    // Now q is the most counterclockwise with respect to p
    // Set p as q for next iteration, to add q to result
    p = q;


} while (p != l);  // While algorithm does not return to first point

// Print Result
int j;
for (j = 0; j < count; j++){
  printf("(%d,%d)\n", results[j].x,results[j].y);
}

}

int main()
{
//declaration for start time, end time
//and total executions for the algorithm
struct timeval start, end;
int i, num_run = 100;

gettimeofday(&start,NULL);

Point points[] = {{0, 3}, {2, 2}, {1, 1}, {2, 1},
                    {3, 0}, {0, 0}, {3, 3}};

int n = sizeof(points)/sizeof(points[0]);

convexHull(points, n);

gettimeofday(&end,NULL);

int cpu_time_used = (((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec 
- start.tv_usec));
printf("\n\nExecution time: %d ms\n", cpu_time_used);
return 0;
}

尝试通过添加以下代码行来使输入足够详细:

Point points[3000];
int i;
for(i=0;i<3000;i++) {
    points[i].x = rand()%100;
    points[i].y = rand()%100;
    int j;
    for(j=i+1;j<3000;j++) {
        if(points[i].x==points[j].x) {
            if(points[i].y==points[j].y) {
            i--; 
            break;
            }
        }
    }
}

但有时会崩溃

【问题讨论】:

  • 对于小型数据集,并行化可能会更慢。原因是创建和管理线程的开销。
  • 我建议尝试另一种您知道高度“可并行化”的算法,或者按照@Osiris 的建议扩大数据集

标签: c openmp convex-hull


【解决方案1】:

在您的以下代码中,并行 for 循环的全部内容被包装到 critical 语句中。这意味着这部分代码一次输入的线程永远不会超过一个线程。让多个线程一次工作一个不会比单个线程经历所有迭代更快。但最重要的是,同步开销会损失一些时间(每个线程必须在进入临界区之前获取一个互斥体,然后再释放它)。

int l = 0,i;
#pragma omp parallel shared (n,l) private (i)
{
    #pragma omp for
    for (i = 1; i < n; i++)
    {
        #pragma omp critical
        {
            if (points[i].x < points[l].x)
            l = i;
        }
    }
}

需要对串行代码进行一些重构以实现并行化。简化通常是简单操作的好方法:让每个线程计算部分迭代的部分结果(例如部分最小值、部分和),而不是将所有结果合并到一个全局结果中。对于支持的操作,可以使用#pragma omp for reduction(op:var) 语法。但在这种情况下,必须手动进行缩减。

了解以下代码如何依赖局部变量来跟踪最小值x 的索引,然后使用单个临界区来计算全局最小值索引。

int l = 0,i;
#pragma omp parallel shared (n,l) private (i)
{
    int l_local = 0; //This variable is private to the thread

    #pragma omp for nowait
    for (i = 1; i < n; i++)
    {
        // This part of the code can be executed in parallel
        // since all write operations are on thread-local variables
        if (points[i].x < points[l_local].x)
            l_local = i;
    }

    // The critical section is entered only once by each thread
    #pragma omp critical
    {
    if (points[l_local].x < points[l].x)
        l = l_local;
    }

    #pragma omp barrier
    // a barrier is needed in case some more code follow
    // otherwise there is an implicit barrier at the end of the parallel region
}

同样的原则应该应用到第二个并行循环中,它会遇到同样的问题,即实际上完全被critical 语句序列化。

【讨论】:

  • 由于变量n 没有被修改,我认为没有理由分享它。将其设为私有可以为各个参与线程提供更轻量级的访问。
  • 然后必须将其标记为firstprivate(n),以便每个线程内的私有副本使用预先存在的变量的值进行初始化。不过,我认为它不会产生太大影响(使用静态调度,主线程将访问其值一次,然后在线程之间分配工作,然后根据它们自己的内部私有变量循环)。
  • 试图通过添加以下代码行来使输入足够实质性:Point points[3000];诠释我; for(i=0;i
猜你喜欢
  • 2012-05-24
  • 1970-01-01
  • 2021-08-28
  • 2011-08-27
  • 2017-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-25
相关资源
最近更新 更多