vectorizing accumulate() when possible ?

Bernard Hugueney bernard@bernard-hugueney.org
Mon Sep 21 12:39:00 GMT 2009


Hi,
While trying to achieve maximum speed for something as trivial as
computing sums of large quantities of numbers, I was wondering whether
it would be possible to vectorize std::accumulate when it makes sense.

Current implementation is completly generic, but I believe that more
often thant not (at least when performance matters), std::accumulate
will be called with primitive numeric data type, on contiguous memory
and with standard arithmetic + or * operators. In this configuration,
a vector of intermediate results could be used to enable the use of
vector (i.e SSE) instructions.

I came up with a small exemple program(*) trying to lift the
vectorizing power of the compiler for operator+ as a proof of
concept.People with better understanding of the vectorizing process
would surely improve it (see comments in code).
I realize that such an implementation unfortunately violates the
std::accumulate() (imvho over-)specifications. However, I believe that
the parallel mode already does violates thoses rules so maybe such
implementation could find its way into parallel mode (or when
-ffastmath is used) ? (Btw, I'd be very interested by informations on
trying to tackle the interactions between vectorizing and
parallelizing such loops with OpenMP even if parallel mode would only
be useful on cores *not* sharing L2 cache for such memory I/O
intensive loops.)

Do you think this kind of specializations could have a place in the
libstdc++ implementation ?

Best regards,

Bernard

(*)
// g++ -o vect_acc vect_acc.cxx -lstdc++ -std=c++0x
-ftree-vectorizer-verbose=99 -O4 -march=native

#include <stdlib.h> //atol to avoid dependance on boost::lexical_cast<>

#include <iterator>
#include <iostream>
#include <numeric>

template<int vect_size, typename In, typename T> T acc(In b, In e, T init){
  int const vect_elts(vect_size/sizeof(T));
  std::size_t n(e-b), r(n%vect_elts);
  T vect[vect_elts];

  // should be unrolled a compile-time
  for(std::size_t i(0); i!=vect_elts; ++i){ vect[i]= 0.;}
  // should also be unrolled at compile time, should also handle
alignment issues so the r values
  // should be taken from beginning and end of sequence accordingly
  for(std::size_t i(0); i!=r; ++i, ++b){ init+= *b; }

  for(; b!=e; b+= vect_elts){
  //should be vectorized
    for(std::size_t i(0); i!= vect_elts; ++i)
      { vect[i] += b[i]; }
  }

  // should be unrolled a compile-time
  for(std::size_t i(0); i!=vect_elts; ++i){ init+=vect[i];}

  return init;
}

int main(int argc, char* argv[]){
  typedef int data_t;
  long n(atol(argv[1])), loops(argc > 2 ? atol(argv[2]):1);
  data_t const* const d= new data_t[n];
  volatile data_t res;// volatile in case optimizer would want to be
to agressive
  if(argc > 3){
    for(long i(0); i!= loops; ++i)
      { res= acc<16*8>(d, d+n, 0.); }
  }else{
    for(long i(0); i!= loops; ++i)
      { res= std::accumulate(d, d+n, 0.); }
  }

  std::cout<< res <<std::endl;
  return 0;
}



More information about the Libstdc++ mailing list