Почему оптимизация GCC не работает с valarrays?
Это простая С++-программа с использованием valarrays:
#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, оба
clang++ -std=gnu++14 -O0 main.cpp && ./a.out
и
clang++ -std=gnu++14 -O3 main.cpp && ./a.out
выдаст тот же правильный результат:
512 256 128
Версия Clang:
$ clang++ --version
clang version 3.8.0 (tags/RELEASE_380/final)
Я также пробовал с GCC 4.9.2 на Debian, где исполняемый файл производит правильный результат.
Является ли это возможной ошибкой в GCC или я делаю что-то неправильно? Может ли кто-нибудь воспроизвести это?
EDIT: мне удалось воспроизвести эту проблему также и на версию GCC 6 на Mac OS для Windows.
Ответы
Ответ 1
valarray
и auto
не смешиваются хорошо.
Создает временный объект, затем применяет к нему operator/
:
const auto& res ( ratios_t::value_type(256) / a );
В libstdС++ valarray
используются шаблоны выражений, поэтому operator/
возвращает легкий объект, который ссылается на исходные аргументы и оценивает их лениво. Вы используете const auto&
, который заставляет шаблон выражения привязываться к ссылке, но не продлевает время жизни временного объекта, на который ссылается шаблон выражения, поэтому, когда происходит оценка, временные выходят за пределы области видимости и ее память был повторно использован.
Это будет нормально работать, если вы выполните:
ratios_t res = ratios_t::value_type(256) / a;
Ответ 2
Это результат неосторожной реализации operator/ (const T& val, const std::valarray<T>& rhs)
(и, скорее всего, других операторов над valarrays) с ленивой оценкой:
#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
Так как в вашем примере левый аргумент оператора деления является временным, результатом является undefined.
UPDATE
Как Джонатан Вакели правильно указал, реализация на основе lazy-оценки становится проблемой в этом примере из-за использования auto
.