为什么 GCC 优化不适用于 valarray

Why does GCC optimization not work with valarrays?

本文关键字:适用于 valarray 不适用 优化 GCC 为什么      更新时间:2023-10-16

这是一个使用valarrays的简单c ++程序:

#include <iostream>
#include <valarray>
int main() {
    using ratios_t = std::valarray<float>;
    ratios_t a{0.5, 1, 2};
    const auto& res ( ratios_t::value_type(256) / a );
    for(const auto& r : ratios_t{res})
        std::cout << r << " " << std::endl;
    return 0;  
}

如果我像这样编译并运行它:

g++ -O0 main.cpp && ./a.out

输出符合预期:

512 256 128 

但是,如果我像这样编译并运行它:

g++ -O3 main.cpp && ./a.out

输出为:

0 0 0 

如果我使用优化参数-O1也会发生同样的情况。

GCC 版本是(Archlinux 中的最新版本):

$ g++ --version
g++ (GCC) 6.1.1 20160707

但是,如果我尝试使用叮当声,两者都

clang++ -std=gnu++14 -O0 main.cpp && ./a.out

clang++ -std=gnu++14 -O3 main.cpp && ./a.out

产生相同的正确结果:

512 256 128 

叮当版本是:

$ clang++ --version
clang version 3.8.0 (tags/RELEASE_380/final)

我也在 Debian 上尝试过使用 GCC 4.9.2,其中可执行文件会产生正确的结果。

这是 GCC 中可能存在的错误还是我做错了什么?任何人都可以重现这一点吗?

编辑:我也设法在Mac OS上的GCC 6的自制版本上重现了该问题。

valarrayauto不能很好地混合。

这将创建一个临时对象,然后对其应用operator/

const auto& res ( ratios_t::value_type(256) / a );

libstdc++ valarray使用表达式模板,以便operator/返回一个轻量级对象,该对象引用原始参数并懒惰地计算它们。 您使用const auto&会导致表达式模板绑定到引用,但不会延长表达式模板引用的临时的生存期,因此当计算发生时,临时已超出范围,并且其内存已被重用。

如果您这样做,它将正常工作:

ratios_t res = ratios_t::value_type(256) / a;

更新:截至今天,GCC 主干将给出此示例的预期结果。我修改了我们的valarray表达式模板,使其不易出错,因此创建悬空引用更难(但仍然不是不可能)。新的实施应该包括在明年的GCC 9中。

这是使用惰性求值粗心地实现operator/ (const T& val, const std::valarray<T>& rhs)(很可能是 valarray 上的其他运算符)的结果:

#include <iostream>
#include <valarray>
int main() {
    using ratios_t = std::valarray<float>;
    ratios_t a{0.5, 1, 2};
    float x = 256;
    const auto& res ( x / a );
    // x = 512;  //  <-- uncommenting this line affects the output
    for(const auto& r : ratios_t{res})
        std::cout << r << " ";
    return 0;
}

注释掉"x = 512"行后,输出为

512 256 128 

取消注释该行,输出将更改为

1024 512 256 

由于在您的示例中,除法运算符的左侧参数是临时参数,因此结果是未定义的。

更新

正如Jonathan Wakely正确指出的那样,由于使用了auto,基于延迟计算的实现在本例中成为一个问题。