2012-01-17 59 views
2

我正在构建一个距离矩阵,每行代表一个点,每列是该点与数据中所有其他点之间的距离,我的算法在顺序中工作得很好。然而,当我尝试并行化它时,我得到了段错误错误。以下是我的并行代码,其中dat是包含我所有数据的映射。任何帮助在这里将不胜感激。段错误openmp错误

map< int,string >::iterator datIt; 
map< int,string >::iterator datIt2; 
map <int, map< int, double> > dist; 
int mycont=0; 
datIt=dat.begin(); 
int size=dat.size(); 
#pragma omp parallel //construct the distance matrix 
{ 
    #pragma omp for 
    for(int i=0;i<size;i++) 
    { 
    datIt2=dat.find((*datIt).first); 
    datIt2++; 
    while(datIt2!=dat.end()) 
    { 
     double ecl=0; 
     int c=count((*datIt).second.begin(),(*datIt).second.end(),delm)+1; 
     string line1=(*datIt).second; 
     string line2=(*datIt2).second; 
     for (int i=0;i<c;i++) 
     { 
     double num1=atof(line1.substr(0,line1.find_first_of(delm)).c_str()); 
     line1=line1.substr(line1.find_first_of(delm)+1).c_str(); 
     double num2=atof(line2.substr(0,line2.find_first_of(delm)).c_str()); 
     line2=line2.substr(line2.find_first_of(delm)+1).c_str(); 
     ecl += (num1-num2)*(num1-num2); 
     } 
     ecl=sqrt(ecl); 
     dist[(*datIt).first][(*datIt2).first]=ecl; 
     dist[(*datIt2).first][(*datIt).first]=ecl; 
     datIt2++; 
    } 
    datIt++; 
    } 
} 
+2

如果您希望我们调试该代码,您至少可以在崩溃时提供堆栈跟踪。 – NPE 2012-01-17 14:27:01

+0

请在发布之前至少缩进您的代码。 – 2012-01-17 14:27:06

回答

3

我不知道这是否是你的代码的唯一问题,但标准容器(如std::map)不是线程安全的,至少如果你写信给他们。因此,如果您有任何对您的maps的写入访问权限,例如dist[(*datIt).first][(*datIt2).first]=ecl;,则需要使用#pragm omp criticalmutexes(omp互斥体,或者如果使用boost或C++ 11,可以使用某种同步结构来封装对地图的任何访问权限或std::mutex是选择太):

//before the parallel: 
omp_lock_t lock; 
omp_init_lock(&lock); 
... 

omp_set_lock(&lock); 
dist[(*datIt).first][(*datIt2).first]=ecl; 
dist[(*datIt2).first][(*datIt).first]=ecl; 
omp_unset_lock(&lock); 
... 

//after the parallel: 
omp_destroy_lock(&lock); 

既然你只能从dat读它应该是不同步化罚款(C++ 11至少,C++ 03没有关于threadsafety任何保证(因为它没有线程的概念),通常应该没问题,但仍然没有同步,但在技术上与其实现相关的行为。

此外,由于您未指定数据共享,所以在parallel区域之外声明的所有变量均默认共享。因此,您对datItdatIt2的写入权限也会出现竞争状态。对于datIt2这可以通过将其指定为私有的,甚至更好的在第一次使用点声明它来避免:

map< int,string >::iterator datIt2=dat.find((*datIt).first); 

解决这个为datIt是有点难度,因为它似乎要遍历地图的整个长度。最简单的方法(这是不使用O(n)提前每次迭代成本过高)似乎对datIt私有副本,进行相应的先进操作(不能保证100%的正确,只是一个快速的轮廓):

#pragma omp parallel //construct the distance matrix 
{ 
    map< int,string >::iterator datItLocal=datIt; 
    int lastIdx = 0; 
    for(int i=0;i<size;i++) 
    { 
     std::advance(datItLocal, i - lastIdx); 
     lastIdx = i; 
     //use datItLocal instead of datIt everytime you reference datIt in the parallel 
    //remove ++datIt 
    } 
} 

这样地图迭代omp_get_num_threads()次,但它应该工作。如果这对于您来说是不可接受的开销,请参阅this answer of mine以获取在openmp中循环使用bidirectional iterator的替代解决方案。

一点题外话:也许我错过了什么,但对我来说似乎是考虑datIt是一个迭代到datdat.find(datIt->first)有点redunant。应该只有一个元素在地图上与给定的键,并datIt指向它,所以这似乎是一个昂贵的方式说datIt2=datIt(纠正我,如果我错了)。

2

除了Girzzly的回答,你没有指定任何私人或共享。这通常意味着你不控制你的内存访问。您必须在进入并行区域时定义datIt firstprivate和datIt2 private,否则每个线程将覆盖它们的共享值,这会导致段错误。

而不是使用锁,我会使用临界区,这是更openmpish。

+0

我一般比较喜欢锁定关键部分,因为我可能希望在代码的其他位置添加对共享数据结构的访问,这些位置可以很容易地用锁来处理,但临界部分却不那么容易不同的关键部分相互同步(据我所知)。不过,我也会将锁代码包装在RAII类中,为了简洁起见,我没有在样本中做这些。我希望你不要介意,如果我把'datIt'加到我的答案 – Grizzly 2012-01-17 16:37:44

+0

那很好。我没有广泛使用OOC代码中的openmp。我不确定你的意思是通过同步对方,因为代码一次执行一个线程。另外,在临界区之前和之后,隐式刷新被调用,所以每个人都是最新的。 – Bort 2012-01-17 16:54:03

+0

我并不是100%确定自己,但是我看到它的方式,如果我从源代码的不同部分访问它,我无法保护对具有“关键部分”的共享结构的访问,因为虽然只有一个线程可以在在同一时间给予关键部分的同时,并不禁止在不同关键部分同时出现线索(我认为,如果看起来真的很浪费)。所以关键部分保护代码中的某个位置,而不是某个数据结构。这对我来说并不是一个好主意。 – Grizzly 2012-01-17 17:05:37