【问题标题】:Parallel computing -- jumbled up output?并行计算——输出混乱?
【发布时间】:2015-11-12 01:49:01
【问题描述】:

我正在尝试学习并行计算的基础知识,但我的计算机遇到了问题。看看我下面的代码。基本上,我想打印出“Hello World!”这一行。对于我的计算机拥有的每个内核。我的电脑有四个核心,所以它应该打印出该行四次。如果我使用注释掉的 'cout' 行而不是 'printf' 行,输出将全部混乱。这是因为 '\n' 转义命令与 "Hello World!" 是分开执行的,所以新行输出会随机出现。 'printf' 行是这个问题的解决方案,因为该行是一次性执行的(而不是像 'cout' 行那样分成几部分)。但是,当我使用“printf”时,我的输出仍然像使用“cout”一样混乱。我不知道为什么会这样。我在另一台计算机上尝试了完全相同的代码,它运行良好。只有我的电脑继续用“printf”混淆输出。我已经通过电子邮件向我的 CS 教授发送了电子邮件,他不知道为什么它会在我的计算机上执行此操作。我知道我在我的计算机上正确设置了 OpenMP。有并行计算经验的人知道为什么这会在我的计算机上搞砸吗?

#include <omp.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;

int main()
{
    #pragma omp parallel
    {
        printf("Hello World!\n");
        //cout << "Hello World!\n" << endl;
    }
    return 0;
}

为了说明我在说什么,这是我在计算机上运行上述代码时的输出:

你好我

世界你好!

rld!

世界你好!

【问题讨论】:

  • 我刚刚尝试使用cout,它完美地工作:ideone.com/dPPQeO
  • endl 部分可能是问题的根源,因为它会刷新输出缓冲区。
  • 这几乎是您在并行运行时应该期望发生的事情。没有什么可以阻止 Core 1 的 printf 在 Core 2 的中间运行。这就是为什么并行编程很难。
  • 在某些实现中,printf 会获得内部锁,因此它可以并行工作,但它纯粹是实现细节,不能依赖
  • @R.Yoder 对我来说 printf 似乎很明显 不是 原子的(“一次执行”),因为您刚刚证明了这一点。

标签: c++ c parallel-processing openmp


【解决方案1】:

对不起,你的教授弄错了。您需要利用互斥或其他一些障碍,以保证不间断地使用共享资源(在本例中为 STDOUT 输出文件)。

无论printfstd::cout::operator&lt;&lt;(),混合输出都是潜在的预期行为。由于它们的设计不同,您看到的行为差异是每个执行持续时间的细微差异。无论哪种情况,您都应该预料到这种行为。

我只是不明白为什么它会对其他人有用。

不是。成为你班级的英雄,并解释它是如何工作的以及如何解决它。告诉他们如此发送他们的爱。 :)

【讨论】:

    【解决方案2】:

    如前所述,假设 printf() 是原子的,不会破坏您的输出,而 std::cout::operator&lt;&lt;() 不是,如果根本错误,就会搞砸。

    然而,这里面仍然有一个(微小的)部分“真相”,只是处于不同的层次。举个例子吧:

    如果我尝试使用 OpenMP“Hello world”C 风格,可能会给出以下结果:

    printf( "Hello from thread %d of %d\n",
             omp_get_thread_num(),
             omp_get_num_threads() );
    

    同样的 C++ 风格可能如下所示:

    std::cout << "Hello from thread " << omp_get_thread_num()
              << " of " << omp_get_num_threads()
              << std::endl;
    

    而两者的本质区别在于,对于printf(),我只调用了一次打印方法,并带有完全准备好的输出字符串,而C++风格的方法将调用std::cout::operator&lt;&lt;() 5次,只有位和可能会或可能不会发送到标准输出的行。 在内部,任何事情都可能发生,我不会尝试做出任何行为。但至少在这里使用printf(),我增加了干净输出的机会,即使我不能保证它。

    这是一个完整的例子:

    #include <iostream>
    #include <stdio.h>
    #include <omp.h>
    
    int main() {
        #pragma omp parallel
        printf( "Hello from thread %d of %d with printf()\n",
                 omp_get_thread_num(),
                 omp_get_num_threads() );
    
        printf( "*** outside of parallel region ***\n" );
    
        #pragma omp parallel
        std::cout << "Hello from thread " << omp_get_thread_num()
                  << " of " << omp_get_num_threads()
                  << " with std::cout"
                  << std::endl;
        return 0;
    }
    

    我的 Linux 笔记本电脑上给了我什么(GCC 5.2):

    ~/tmp$ g++ -fopenmp stdout.cc
    ~/tmp$ ./a.out 
    Hello from thread 3 of 4 with printf()
    Hello from thread 0 of 4 with printf()
    Hello from thread 2 of 4 with printf()
    Hello from thread 1 of 4 with printf()
    *** outside of parallel region ***
    Hello from thread Hello from thread Hello from thread Hello from thread 1 of 4 with std::cout23 of 4 with std::cout
     of 4 with std::cout
    0 of 4 with std::cout
    
    ~/tmp$
    

    如果您仔细观察,您会发现对 std::cout::operator&lt;&lt;() 的单独调用都没有被拆分,但每个新调用都是各种线程相互竞争的机会,并使输出受到破坏。

    再说一遍,告诉printf() 是原子的并且不会搞砸是错误的,但简单地说,对于复杂的输出字符串,它比std::cout 更不可能被破坏。

    【讨论】:

      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      相关资源
      最近更新 更多