OpenMP – 为什么比较次数会减少?

我有以下算法:

int hostMatch(long *comparisons) { int i = -1; int lastI = textLength-patternLength; *comparisons=0; #pragma omp parallel for schedule(static, 1) num_threads(1) for (int k = 0; k <= lastI; k++) { int j; for (j = 0; j  i) i = k; } return i; } 

更改num_threads我得到以下比较数的结果:

  • 01 = 9949051000
  • 02 = 4992868032
  • 04 = 2504446034
  • 08 = 1268943748
  • 16 = 776868269
  • 32 = 449834474
  • 64 = 258963324

为什么比较次数不一定? 这很有趣,因为比较的数量减少了一半,线程数量增加了一倍。 是否存在某种竞争条件(*comparisons)++ ,如果变量正在使用,OMP只会跳过增量?

我目前的理解是k循环的迭代在线程中几乎均匀地分开。 每次迭代都有一个私有整数j以及整数k的私有副本,以及一个非并行for循环,它会在终止之前添加到比较中。

你自己说(*comparisons)++; 有竞争条件。 这是一个必须序列化的关键部分(我不认为(*指针)++是一个primefaces操作)。

所以基本上你用两个线程两次读取相同的值(即2),然后增加它(3)并将其写回。 所以你得到3而不是4.你必须确保对并行化函数/循环的本地范围内的变量的操作不重叠。

围绕竞争条件的天真方式将操作声明为atomic update

 #pragma omp atomic update (*comparisons)++; 

请注意,此处的关键部分是不必要的且更昂贵。 可以在具有标量类型的任何l值表达式上对原始二进制或一元操作声明atomic update

然而,这仍然不是最佳的,因为*comparisons的值需要一直在CPU缓存之间移动,并且执行昂贵的锁定指令。 相反,你应该使用减少。 为此你需要另一个局部变量,指针在这里不起作用。

 int hostMatch(long *comparisons) { int i = -1; int lastI = textLength-patternLength; long comparisons_tmp = 0; #pragma omp parallel for reduction(comparisons_tmp:+) for (int k = 0; k <= lastI; k++) { int j; for (j = 0; j < patternLength; j++) { comparisons_tmp++; if (textData[k+j] != patternData[j]) { j = patternLength+1; //break } } if (j == patternLength && k > i) i = k; } *comparisons = comparisons_tmp; return i; } 

PS schedule(static, 1)似乎是一个坏主意,因为这将导致textData上的低效内存访问模式。 只是把它留下来让编译器做它的事情。 如果测量显示它不能有效工作,请给它一些更好的提示。