首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Clang + OpenMP低效循环不变量

Clang + OpenMP低效循环不变量
EN

Stack Overflow用户
提问于 2022-02-28 12:16:21
回答 1查看 138关注 0票数 2

在回答一个不同的问题(如何使用openmp对此代码进行并行化?)时,我遇到了Clang的一些低效率的代码生成。

让我们考虑以下简单的代码:

代码语言:javascript
复制
void scale(float* inout, ptrdiff_t n, ptrdiff_t m, ptrdiff_t stride, float value)
{
    const float inverse = 1.f / value;
#   pragma omp parallel for
    for(ptrdiff_t i = 0; i < n; ++i) {
#       pragma omp simd
        for(ptrdiff_t j = 0; j < m; ++j)
            inout[i * stride + j] *= inverse;
    }
}

你把逆的计算放哪儿了,它重要吗?我已经探讨过的选择:

  1. 在循环之外,它在示例中的位置
  2. 在并行段中,但在循环之前
  3. 在外环
  4. 在内环

对于GCC-11,选项1生成最佳代码:一个除法,然后一个内存负载和每个线程的广播。选项2-4都生成基本相同的代码,每个线程执行一次除法.

Clang组件

然而,使用Clang-13的代码却有很大的不同。

选项1:在内部循环中加载冗余内存并广播。它不通过堆栈指针加载,而是浪费一个通用寄存器作为指向常量的指针。如果将代码更改为需要多个常量,Clang将浪费多个GP寄存器。

选项2:与GCC相同的代码模式

选项3:在外部循环的每次迭代中重复一次除法

选项4:重复内部循环中的除法

摘要

似乎Clang的代码生成在从OpenMP循环中提取冗余计算时遇到了一些问题。有趣的是,它似乎不影响数组索引计算。把它从内部循环中拉出来很好。

如果我想要对GCC和Clang都有效的代码,我必须编写如下代码:

代码语言:javascript
复制
void scale(float* inout, ptrdiff_t n, ptrdiff_t m, ptrdiff_t stride, float value)
{
#   pragma omp parallel
    {
        const float inverse = 1.f / value;
#       pragma omp for nowait
        for(ptrdiff_t i = 0; i < n; ++i) {
#           pragma omp simd
            for(ptrdiff_t j = 0; j < m; ++j)
                inout[i * stride + j] *= inverse;
        }
    }
}

但这是非常冗长的。这在这个代码示例中是一个小麻烦,但是如果您查看上面另一个答案中的代码,它会变得非常糟糕(尤其是GP寄存器浪费),严重影响性能。

总之,我是不是漏掉了什么?我是否应该以不同的方式编写循环,以确保Clang和GCC都有好的代码?

补充信息

下面是一个允许简单测试的代码版本,下面是一个螺栓连接

代码语言:javascript
复制
#include <cstddef>
// using std::ptrdiff_t

#define CONST_LOCATION 1

void scale(float* inout, std::ptrdiff_t n, std::ptrdiff_t m, std::ptrdiff_t stride,
           float value)
{
# if CONST_LOCATION == 1
    /*
     * Clang-13.0.1: Redundant broadcast from memory in inner loop.
     *               Wastes GP register for pointer to constant
     * GCC-11.2: Optimal
     */
    const float inv = 1.f / value;
  #endif
#   pragma omp parallel
    {
#     if CONST_LOCATION == 2
        /*
         * Clang: Redundant computation in outer loop setup. Otherwise optimal
         * GCC: Same as Clang
         */
        const float inv = 1.f / value;
#     endif
#       pragma omp for nowait
        for(std::ptrdiff_t i = 0; i < n; ++i) {
#         if CONST_LOCATION == 3
            /*
             * Clang: Redundant computation in inner loop setup!
             * GCC: Same as 2
             */
            const float inv = 1.f / value;
#         endif
#           pragma omp simd
            for(std::ptrdiff_t j = 0; j < m; ++j) {
#             if CONST_LOCATION == 4
                /*
                 * Clang: Redundant computation in inner loop!
                 * GCC: Same as 2
                 */
                const float inv = 1.f / value;
#             endif
                inout[i*stride + j] *= inv;
            }
        }
    }
}

-O3 -mavx2 -mfma -fopenmp进行测试,以获得合理的通用、现代编译。

EN

回答 1

Stack Overflow用户

发布于 2022-02-28 15:47:04

要回答我自己,是内部循环中的pragma omp simd混淆了clang的代码生成。这是令人遗憾的,因为在某些情况下,它确实对某些编译器产生了积极影响。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71294894

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档