Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC sometimes doesn't inline std::array::operator[]

I have a complex program which uses std::array<double, N> for small values of N. It uses operator[] to get values from these arrays.

I have discovered that GCC 6.1 with -O2 or -O3 does not inline these calls, leading to these C++ arrays being slower than their C equivalents.

Here's the generated assembly:

340 <std::array<double, 8ul>::operator[](unsigned long) const>:

340:  48 8d 04 f7             lea    (%rdi,%rsi,8),%rax
344:  c3                      retq   
345:  90                      nop
346:  66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
34d:  00 00 00 

The very same code is emitted for each size of array (since there is no bounds checking).

A loop over such an array looks like this:

4c0:  e8 7b fe ff ff          callq  340 <std::array<double, 8ul>::operator[](unsigned long) const>
4c5:  be 07 00 00 00          mov    $0x7,%esi
4ca:  4c 89 f7                mov    %r14,%rdi
4cd:  48 89 44 24 78          mov    %rax,0x78(%rsp)

...6 more copies of this...

4d2:  e8 69 fe ff ff          callq  340 <std::array<double, 8ul>::operator[](unsigned long) const>
4d7:  48 89 44 24 70          mov    %rax,0x70(%rsp)
4dc:  31 f6                   xor    %esi,%esi
4de:  4c 89 ef                mov    %r13,%rdi

This seems obviously bad. The problem is that small test programs don't elicit this behavior.

So my question is: how can I get GCC to tell me why it isn't inlining these one-instruction calls, and/or make it inline them? Obviously I can't modify the <array> header file to add __attribute__((inline)).

like image 458
John Zwinck Avatar asked Oct 29 '22 11:10

John Zwinck


1 Answers

There appears to be a bug in the optimizer of GCC 5 and 6 which manifests when using __attribute__(("unroll-loops")) in combination with -ffast-math or related options.

You can see it in action here: https://godbolt.org/g/ZBGCDB

This code reproduces the error if you compile with -O3 -ffast-math:

#include <array>

typedef std::array<double, 2> Array;

void foo(Array& a) __attribute__((optimize("unroll-loops")));

void foo(Array& a)
{
  for (size_t ii = 0; ii < a.size(); ++ii)
    a[ii] = 1.0;
}

It works as expected without -ffast-math, or if compiled with GCC 4.9, GCC 7 or later, or Clang.

like image 185
John Zwinck Avatar answered Nov 15 '22 06:11

John Zwinck