使用openmp时,英特尔C编译器给出错误的输出

埃莉诺拉

我正在尝试第一次使用Intel C编译器,并且得到的答案完全错误。我究竟做错了什么?

我有一些代码如下:

#include <stdint.h>
#include <stdio.h>

#define CHUNK_SIZE 12
#define NUM_THREADS 8

#define popcnt __builtin_popcountll
#define BILLION (1000 * 1000 * 1000)
#define UPDATE_ROW_PPROD() \
    update_row_pprod(row_pprod, row, rows, row_sums, mask, mask_popcnt)

typedef __int128 int128_t;

static inline int64_t update_row_pprod
(
    int64_t* row_pprod, int64_t row, int64_t* rows,
    int64_t* row_sums, int64_t mask, int64_t mask_popcnt
)
{
    int64_t temp = 2 * popcnt(rows[row] & mask) - mask_popcnt;

    row_pprod[0] *= temp;
    temp -= 1;
    row_pprod[1] *= temp;
    temp -= row_sums[row];
    row_pprod[2] *= temp;
    temp += 1;
    row_pprod[3] *= temp;

    return row + 1;
}

int main(int argc, char* argv[])
{
    int64_t size = argc - 1, rows[argc - 1];
    int64_t row_sums[argc - 1];
    int128_t permanent = 0, sign = size & 1 ? -1 : 1;

    if (argc == 2)
    {
        printf("%d\n", argv[1][0] == '-' ? -1 : 1);
        return 0;
    }

    for (int64_t row = 0; row < size; row++)
    {
        char positive = argv[row + 1][0] == '+' ? '-' : '+';

        sign *= ',' - positive;
        rows[row] = row_sums[row] = 0;

        for (char* p = &argv[row + 1][1]; *p; p++)
        {
            rows[row] <<= 1;
            rows[row] |= *p == positive;
            row_sums[row] += *p == positive;
        }

        row_sums[row] = 2 * row_sums[row] - size;
    }

    #pragma omp parallel for reduction(+:permanent) num_threads(NUM_THREADS)
    for (int64_t mask = 1; mask < 1LL << (size - 1); mask += 2)
    {
        int64_t mask_popcnt = popcnt(mask);
        int64_t row = 0;
        int128_t row_prod = 1 - 2 * (mask_popcnt & 1);
        int128_t row_prod_high = -row_prod;
        int128_t row_prod_inv = row_prod;
        int128_t row_prod_inv_high = -row_prod;

        for (int64_t chunk = 0; chunk < size / CHUNK_SIZE; chunk++)
        {
            int64_t row_pprod[4] = {1, 1, 1, 1};

            for (int64_t i = 0; i < CHUNK_SIZE; i++)
                row = UPDATE_ROW_PPROD();

            row_prod *= row_pprod[0], row_prod_high *= row_pprod[1];
            row_prod_inv *= row_pprod[3], row_prod_inv_high *= row_pprod[2];
        }

        int64_t row_pprod[4] = {1, 1, 1, 1};

        while (row < size)
            row = UPDATE_ROW_PPROD();

        row_prod *= row_pprod[0], row_prod_high *= row_pprod[1];
        row_prod_inv *= row_pprod[3], row_prod_inv_high *= row_pprod[2];
        permanent += row_prod + row_prod_high + row_prod_inv + row_prod_inv_high;
    }

    permanent *= sign;

    if (permanent < 0)
        printf("-"), permanent *= -1;

    int32_t output[5], print = 0;

    output[0] = permanent % BILLION, permanent /= BILLION;
    output[1] = permanent % BILLION, permanent /= BILLION;
    output[2] = permanent % BILLION, permanent /= BILLION;
    output[3] = permanent % BILLION, permanent /= BILLION;
    output[4] = permanent % BILLION;

    if (output[4])
        printf("%u", output[4]), print = 1;
    if (print)
        printf("%09u", output[3]);
    else if (output[3])
        printf("%u", output[3]), print = 1;
    if (print)
        printf("%09u", output[2]);
    else if (output[2])
        printf("%u", output[2]), print = 1;
    if (print)
        printf("%09u", output[1]);
    else if (output[1])
        printf("%u", output[1]), print = 1;
    if (print)
        printf("%09u\n", output[0]);
    else
        printf("%u\n", output[0]);
}

如果我用它编译

gcc -Wall -std=c99 -fopenmp  -o permanent permanent.c

然后我可以运行它

permanent -+ -+

并获得输出

-2

哪个是对的。

如果我改用Intel C编译器(17.0.1)进行编译

icc -std=c99 -qopenmp -Wall permanent.c

然后做

a.out -+ -+

我懂了

11910984139051480114196905982

如注释中所述,如果删除-qopenmp,则icc会生成一个可以正常运行的版本,尽管该版本只能在一个内核上运行。

Zulan

看起来像英特尔在处理__int128类型变量的OpenMP缩减中的错误这很容易复制:

#include <stdio.h>
#include <inttypes.h>

int main() {
    __int128 sum = 0;
    #pragma omp parallel for reduction(+:sum)
    for (int i = 0; i < 0; i++) {
        sum += 1;
    }
    printf("%" PRIX64 " %" PRIX64 "\n", (uint64_t)sum, (uint64_t)(sum >> 64));
}

输出-fopenmp

14000000000 78778300

正确输出不带或带gcc -fopenmp

0 0

您可以__int128使用简单的自定义归约声明来解决该问题(并保留类型):

#pragma omp declare reduction(add128: int128_t: omp_out = omp_out + omp_in) initializer(omp_priv = 0)
[...]
#pragma omp parallel for reduction(add128:permanent) num_threads(NUM_THREADS)

据我所知,该标准并不限制减少清单元素的类型。至少编译器应该告诉您是否不支持它。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

英特尔C ++编译器给出“此平台不支持卸载结构”错误

英特尔编译器(C ++)在std :: vector上具有OpenMP减少问题

英特尔编译器使用错误的头

英特尔C ++编译器:如何在宏定义/定义中编译和链接openmp编译指示?

在 Windows 上使用英特尔 C++ 编译器无法执行程序

英特尔编译器:将fortran编译库链接到C的主程序

无法在 Windows 上使用英特尔编译器编译示例 boost::multiprecision

使用英特尔 Fortran 编译器编译独立的 DLL 文件

英特尔编译器内联大小

英特尔TBB-'InitializeCriticalSectionEx':找不到标识符编译器错误

英特尔Fortran编译器2015(Mac OS X)的TYPE(*)语法错误

英特尔编译器-错误:标识符“ alignof”未定义

英特尔编译器返回“预期的语句结尾时找到格式_元素”

在哪里可以下载英特尔C ++编译器?

英特尔C ++编译器-常量字符串可修改

为什么英特尔编译器会忽略英特尔MIC的非临时预取编译指示?

为什么额外的-I标志(包含目录)会中断编译?(使用英特尔编译器)

如何使用英特尔Windows编译器在命令行上禁用C ++ 0x和/或C ++ 11?

这里有人对基准英特尔C ++编译器和GCC进行过测试吗?

有效使用英特尔编译器SVML`__m128 _mm_sincos_ps()`

英特尔编译器18.0工具集引发msbuild异常

Visual Studio 中的英特尔编译器找不到 boost 库

英特尔Fortran编译器找不到模块`mpi`

带有 gcc 7.1/8.1 的英特尔编译器 17.0

英特尔Fortran编译器(ifort)禁止显示特定警告消息

尝试使用继承定义类时,编译器给出错误

使用英特尔 MKL 编译 C 代码:ld:未知选项:--no-as-needed

为什么VC ++编译器在复合运算符重载时给出错误?

在UI元素上右键单击NOT时,为什么编译器会给出错误