Why does GCC optimization not work with valarrays?

2019-03-25 06:26发布

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.

2条回答
混吃等死
2楼-- · 2019-03-25 06:44

It's the result of careless implementation of operator/ (const T& val, const std::valarray<T>& rhs) (and most probably other operators over valarrays) using lazy evaluation:

#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;
}

With the "x = 512" line commented out, the output is

512 256 128 

Uncomment that line and the output changes to

1024 512 256 

Since in your example the left-hand side argument of the division operator is a temporary, the result is undefined.

UPDATE

As Jonathan Wakely correctly pointed out, the lazy-evaluation based implementation becomes a problem in this example due to the usage of auto.

查看更多
时光不老,我们不散
3楼-- · 2019-03-25 06:46

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.

查看更多
登录 后发表回答