循环内的OpenMP同步

Eug*_*ith 5 c openmp

我有一个生成数据的大循环.每次迭代需要1秒钟,然后产生一大块数据.我需要以正确的顺序写入文件的所有块.

如果我只想并行化循环,我可以写这样的东西(高度简化):

    FILE* f = fopen("output.txt", "w");
    omp_lock_t lock;
    omp_init_lock(&lock);
    int nIterations = 1000000;
#pragma omp parallel for
    for(int thread=0; thread<4; thread++)
    {
        int a=0, b=0, c=0;
        for(int n=thread; n<nIterations; n+=4)
        {
            int value = do_computations(&a, &b, &c);
            omp_set_lock(&lock);
            fprintf(f, "%d\n", value);
            omp_unset_lock(&lock);
        }
    }
#pragma omp barrier
    fclose(f);
    omp_destroy_lock(&lock);
Run Code Online (Sandbox Code Playgroud)

这会将我的输出输入到文件中,但不保证条目的顺序.

我想同步执行,以便所有线程完成他们的任务,然后主线程写入文件,然后线程恢复.换句话说,我想要这样的事情:

    #pragma omp parallel for
        for(int thread=0; thread<4; thread++)
        {
            int a=0, b=0, c=0;
            int values[4];
            for(int n=thread; n<nIterations; n+=4)
            {
                values[n] = do_computations(&a, &b, &c);
#pragma omp barrier
                if(thread == 0)
                {
                      for(int i=0; i<4; i++)
                        fprintf(f, "%d\n", values[i]);
                }
#pragma omp barrier
            }
        }
#pragma omp barrier
Run Code Online (Sandbox Code Playgroud)

除了一些莫名其妙的原因,这是OpenMP规范禁止的.

或者我可以试试

    #pragma omp parallel for
        for(int thread=0; thread<4; thread++)
        {
            int a=0, b=0, c=0;
            for(int n=thread; n<nIterations; n+=4)
            {
                int value = do_computations(&a, &b, &c);
#pragma omp ordered
                {
                    fprintf(f, "%d\n", value);
                }
            }
        }
    #pragma omp barrier
        fclose(f);
Run Code Online (Sandbox Code Playgroud)

但这也不会起作用,因为"带有for构造的循环的迭代......不能执行多个有序指令."

我不想将代码重写为单个循环,我不想交换循环.

有没有一种干净的方法来使用OpenMP,没有其他线程/同步工具?

Jon*_*rsi 3

你正在尝试做两件事——计算和 IO。计算可以并行,但IO必须是串行的。但是,通过将 IO 放在与计算相同的循环中,您也会强制计算序列化,这是没有意义的。

你最好先完成所有计算,然后再进行 IO。即使是串行方式,这几乎肯定会更快,特别是如果您可以将数据以二进制形式写入一大块,而不是通过 fprintfs 进行循环。

    FILE* f = fopen("output.txt", "w");
    const int nIterations = 1000000;
    int values[nIterations];

#pragma omp parallel for 
    for(int n=0; n<niterations; n++)
    {
        int a=0, b=0, c=0;
        values[n] = do_computations(&a, &b, &c);
    }

    for (int n=0; n<niterations; n++)
         fprintf(f,"%d\n", values[n]);

    fclose(f);
Run Code Online (Sandbox Code Playgroud)

当然,这需要更多的内存,但速度与内存是一个常见的权衡。如果这种权衡的极端不起作用,您始终可以以可调整大小的块进行计算:

    const int nIterations = 1000000;
    const int chunkSize   = 10000;
    int values[chunkSize];
    int chunkNum = 0;
    int chunkLeft = chunkSize;

    for (int start = 0; start < nIterations; start+= chunkSize) {

        if (start+chunkSize > nIterations) chunkLeft = nIterations - start;

    #pragma omp parallel for 
        for(int n=start; n<start+chunkLeft; n++)
        {
            int a=0, b=0, c=0;
            values[n-start] = do_computations(&a, &b, &c);
        }

        for (int n=0; n<chunkLeft; n++)
             fprintf(f,"%d\n", values[n]);

    }
    fclose(f);
Run Code Online (Sandbox Code Playgroud)