什么是具有 ID 相关向后分支的循环?

alf*_*lfC 11 c++ for-loop clang-tidy

我试图理解这个铿锵有力的警告:altera-id-dependent-backward-branch这似乎是由这个循环触发的。

        for(; first != current; ++first)
Run Code Online (Sandbox Code Playgroud)

我的例子是这段代码,它看起来几乎完全是std::uninitialized_fill_n.

静态分析器抱怨说:

error: backward branch (for loop) is ID-dependent due to variable reference to 'current' and may cause performance degradation [altera-id-dependent-backward-branch,-warnings-as-errors]
                        for(; current != first; ++first) {
Run Code Online (Sandbox Code Playgroud)
uninitialized_fill_n(ForwardIt first, Size n, T const& v) {
    ForwardIt current = first;
    try {
        for(; n > 0; ++current, --n) {
            new (std::addressof(*current)) T(v);
        }
        return current;
    } catch(...) {
        for(; first != current; ++first) {  // clang-tidy error here
            std::addressof(*first)->~T();
        }
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试了不同的方法来重写代码(例如向后循环),以抑制此警告,但我做不到。 是否有一种标准方法可以以可以接受的方式重写后备(catch)循环altera-id-dependent-backward-branch

我在用clang-tidy 13.0.0


我还在我的代码库中发现了相同警告的另一个实例(这是由 Howard Hinnart 编写的哈希函数的实现):

auto fnv1a(void const* key, std::size_t len, std::size_t h) noexcept {
    auto const *p = static_cast<unsigned char const*>(key);
    unsigned char const* const e = p + len;
    for(; p < e; ++p) {  // warning here
        h = (h ^ *p) * 1099511628211U; // prime
    }
    return h;
} 
Run Code Online (Sandbox Code Playgroud)

小智 3

altera-id-dependent-backward-branch警告源自 clang-tidy 的 FPGA 特定规则。具体来说,当针对 FPGA 时,编译器将 C++ 转换为硬件,而硬件与传统 CPU 相比具有不同的性能特征。

您看到的警告是因为循环中的分支取决于归纳变量(current在第一种情况和p第二种情况下)。将循环综合到硬件中时,依赖于归纳变量的分支可能会导致更长的管道停顿。确切的影响是特定于硬件的,但对于高性能循环,影响可能并不小。

为了解决该警告,循环的终止条件需要独立于归纳变量。

让我们从第一个例子开始:

for(; first != current; ++first) {  // clang-tidy error here
    std::addressof(*first)->~T();
}
Run Code Online (Sandbox Code Playgroud)

您可以提前确定迭代次数,然后使用固定循环界限:

auto distance = std::distance(first, current);
for(auto i = 0; i < distance; ++i, ++first) {
    std::addressof(*first)->~T();
}
Run Code Online (Sandbox Code Playgroud)

对于第二个例子:

for(; p < e; ++p) {  // warning here
    h = (h ^ *p) * 1099511628211U; // prime
}
Run Code Online (Sandbox Code Playgroud)

您可以将其重写为:

auto distance = e - p;
for(std::size_t i = 0; i < distance; ++i, ++p) {
    h = (h ^ *p) * 1099511628211U; // prime
}
Run Code Online (Sandbox Code Playgroud)

此转换确保循环边界是固定的并且不依赖于归纳变量。因此,FPGA 合成器应该能够更轻松地创建高效的流水线。

也就是说,您应该始终测试综合结果以验证性能是否满足您的期望。根据特定的硬件和循环的复杂性,可能仍然存在性能权衡。