为什么 GCC 优化不适用于 valarray
Why does GCC optimization not work with valarrays?
这是一个使用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的自制版本上重现了该问题。
valarray
和auto
不能很好地混合。
这将创建一个临时对象,然后对其应用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
,基于延迟计算的实现在本例中成为一个问题。
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 适用于 WebView2 旧版本的示例应用程序
- 在 NVIDIA GEFORCE GTX 1050 上下载适用于 Windows 10 的 openCL 1.2
- __attribute__(优化(0))) 是否适用于"recursively"?
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使用一个参数的模板函数时出错(适用于 2)
- 使用 适用于 Android 和 iOS 的 tf-lite C++ API
- 为什么这适用于 G++ 而不是 CLANG?
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- 适用于 Linux 的 c++ 上的代理脚本
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 有没有适用于Windows.lib文件的GNU二进制文件描述符(BFD)
- 模板函数仅适用于VS
- 如何在cmake中包含适用于g++或viceversa的库
- 适用于win32、linux、mac的POSIX C包装器
- WinDBG适用于从Visual Studio 2015保存的转储,但不适用于任务管理器。显示异常代码"not found"
- 从uint8_t到NPY_UINT16 PyArray_SimpleNewFromData.适用于Linux,但不适用于