Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does GCC optimization not work with valarrays?

Tags:

c++

gcc

g++

clang++

This is a simple c++ program using 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;  
}

If I compile and run it like this:

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

The output is as expected:

512 256 128 

However, if I compile and run it like this:

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

The output is:

0 0 0 

Same happens if I use -O1 optimization parameter.

GCC version is (latest in Archlinux):

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

However, if I try with clang, both

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

and

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

produce the same correct result:

512 256 128 

Clang version is:

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

I've also tried with GCC 4.9.2 on Debian, where executable produces the correct result.

Is this a possible bug in GCC or am I doing something wrong? Can anyone reproduce this?

EDIT: I managed to reproduce the issue also on Homebrew version of GCC 6 on Mac OS.

like image 667
DoDo Avatar asked Jul 13 '16 14:07

DoDo


1 Answers

valarray and auto do not mix well.

This creates a temporary object, then applies operator/ to it:

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

The libstdc++ valarray uses expression templates so that operator/ returns a lightweight object that refers to the original arguments and evaluates them lazily. You use const auto& which causes the expression template to be bound to the reference, but doesn't extend the lifetime of the temporary that the expression template refers to, so when the evaluation happens the temporary has gone out of scope, and its memory has been reused.

It will work fine if you do:

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

Update: as of today, GCC trunk will give the expected result for this example. I've modified our valarray expression templates to be a bit less error-prone, so that it's harder (but still not impossible) to create dangling references. The new implementation should be included in GCC 9 next year.

like image 82
Jonathan Wakely Avatar answered Sep 28 '22 02:09

Jonathan Wakely