【问题标题】:OpenMP: pragma cancel for ON NUMAOpenMP:ON NUMA 的编译指示取消
【发布时间】:2016-11-13 07:32:02
【问题描述】:

----------编辑--------------

我将代码编辑如下:

#pragma omp parallel for private(i, piold, err) shared(threshold_err) reduction(+:pi) schedule (static)
{
  for (i = 0; i < 10000000000; i++){ //1000000000//705035067
        piold = pi;
        pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
        err = fabs(pi-piold);
        if ( err < threshold_err){
#pragma omp cancel for
        }

  }
}
  pi = 4*pi;

我用 LLVM3.9/Clang4.0 编译它。当我用一个线程运行它时,我会通过 pragma cancel 操作获得预期的结果(检查非 pragma cancel 版本,导致运行速度更快)。

但是当我使用线程 >=2 运行它时,程序进入循环。我在 NUMA 机器上运行代码。怎么了?可能取消条件不满足!但是代码比单线程非pragma-cancel版本需要更长的时间!!仅供参考,它在 OMP_CANCELLATION=false 时运行文件。


我有以下 OpenMP 代码。我正在使用 LLVM-3.9/Clang-4.0 来编译这段代码。

#pragma omp parallel private(i, piold, err) shared(pi, threshold_err)
{
#pragma omp for reduction(+:pi) schedule (static)
  for (i = 0; i < 10000000 ; i++){
        piold = pi;
        pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
        #pragma omp critical
        {
        err = fabs(pi-piold);// printf("Err: %0.11f\n", err);
        }
        if ( err < threshold_err){
                printf("Cancelling!\n");
                #pragma omp cancel for
        }

  }
}

不幸的是,我认为#pragma omp cancel for 不会终止整个for 循环。最后我打印出err 值,但再次使用并行性,它会混淆正在打印的值。 err 的最终值小于threshold_err。打印取消是打印但在程序的最开始,这是令人惊讶的。之后程序继续运行!

如何确保这是正确的实现?顺便说一句,OMP_CANCELLATION 设置为 true,并且一个小型测试程序为相应的函数 omp_get_cancellation() 返回“1”。

【问题讨论】:

    标签: c++ openmp cancellation numa


    【解决方案1】:

    我知道 omp cancel 只是一个中断信号,它会通知以后不会创建线程。仍在运行的线程将持续到结束。见http://bisqwit.iki.fi/story/howto/openmp/http://jakascorner.com/blog/2016/08/omp-cancel.html

    事实上,在我看来,我认为您的程序产品是可以接受的近似值。但是,一些变量可以保持在较小的范围内。这是我的建议

    #include <iostream>
    #include <cmath>
    #include <iomanip>
    
    int main() {
    
        long double pi = 0.0;
        long double threshold_err = 1e-7;
        int cancelFre = 0;
    
    #pragma omp parallel shared(pi, threshold_err, cancelFre)
        {
    #pragma omp for reduction(+:pi) schedule (static)
            for (int i = 0; i < 100000000; i++){
                long double piold = pi;
                pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
                long double err = std::fabs(pi-piold);
                if ( err < threshold_err){
    
    #pragma omp cancel for
                   cancelFre++;
                }
    
            }
        }
    
        std::cout << std::setprecision(10) << pi * 4 << " " << cancelFre;
    
        return 0;
    }
    

    【讨论】:

    • 另外,当我删除 cancel for 循环部分时,i = 100000000 的代码大约需要 11 秒。但是有了cancel for,我必须永远中止它。
    • @algoProg cancelFre 是取消调用频率,我把它放在 ompcancel 之后计算取消信号发送的次数(所以我知道它毕竟只是一个信号)。我很确定我可以通过循环。我还在我的笔记本电脑上确认您的代码也可以工作。我只是看到你分享了很多变量。
    • 当我运行它时,我得到这个错误:/tmp/stackoverflow_cancel-6237c0.o: In function .omp_outlined.': stackoverflow_cancel.cpp:(.text+0x408): undefined reference to __sync_val_compare_and_swap_16'
    • 另外,当我运行我的代码时,我看到取消编译指示减少了时间,但我没有看到随着线程数的增加而缩放。所有线程值始终相同
    【解决方案2】:

    好的,我解决了。在我上面的代码中,问题就在这里:

    err = fabs(pi-piold);

    在上面的行中,pi 在下面的 if 条件改变之前被改变。多个线程也做同样的事情。据我了解,这会使程序陷入僵局。

    我通过强制只有一个线程(master)来解决它:

    if(omp_get_thread_num()==0){
    err = fabs(pi-piold);
    if ( err < threshold_err){
    #pragma omp cancel for
            }
    }
    

    我本可以使用#pragma omp single,但它给出了关于嵌套编译指示的错误。

    这里的性能在线程数较少时会受到影响(1-4 比正常的顺序代码差)。之后性能提高。这不是最好的解决方案,肯定有人可以改进这个解决方案。

    【讨论】:

    • 我仍然不明白为什么你更喜欢让 i 和 piold 在循环之外,同时它们可以在循环范围内。如果它们只是在循环内,则不必担心竞争条件,因为它们在每个循环中都是分开的。
    • 请看 khôi nguyễn 的回答。有用!!基本上在并行编译指示中定义ipiold。尽管 1-4 个线程的性能仍然不如顺序。 5-16 线性扩展(16 是最大线程数)。
    猜你喜欢
    • 1970-01-01
    • 2011-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-03
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多