并行计算--混乱的输出

Parallel computing -- jumbled up output?

本文关键字:输出 混乱 并行计算      更新时间:2023-10-16

我正在努力学习并行计算的基础知识,但我的计算机遇到了问题。看看下面的代码。基本上,我想打印出一行";你好,世界"我电脑的每一个核心。我的电脑有四个核心,所以它应该把那行打印四次。如果我使用注释掉的"cout"行而不是"printf"行,那么输出就会混乱不堪。这是因为"\n"转义命令是与";你好,世界&";,因此新的线路输出将随机发生。"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!

你好,世界!

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

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

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

事实并非如此。成为班上的英雄,解释它是如何工作的以及如何修复它。告诉他们SO表达他们的爱。:)

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

然而,这其中仍然有一部分(微小的)"真理",但在不同的层面上。让我给你举个例子:

如果我尝试一个OpenMP"Hello world"C风格,可能会给出以下结果:

printf( "Hello from thread %d of %dn",
         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<<() 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<<()的单独调用都没有被拆分,但每个新调用都是各个线程相互竞争的机会,并使输出受到破坏。

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