Skip to main content

Avoiding intermediate copies in std::accumulate

std::accumulate makes a ton of copies internally. In fact it's 2x the size of the number of elements in the iterator range. To fix, use std::ref and std::reference_wrapper for the initial state. std::shared_ptr is also a possibility if the accumulated state must be dynamically allocated for some reason. Live code on wandbox.

Update: Please see alternative solutions in the comments section.

#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <string>
#include <numeric>
#include <functional>

struct Vector : public std::vector<int> {
  Vector(std::initializer_list<int> il) : std::vector<int>(il){
    std::cout << "Vector(std::initializer_list)\n";
  }
  Vector() {
    std::cout << "Vector()\n";
  }
  Vector(const Vector &v) : std::vector<int>(v) {
    std::cout << "Vector(const Vector &)\n";
  }
  Vector & operator = (const Vector &v) {
    if (this != &v) {
      std::vector<int>::operator=(v);
      std::cout << "Vector& Vector::operator=(const Vector &)\n";
    }
    return *this;
  }
  Vector & operator = (Vector && v) {
    if (this != &v) {
      std::vector<int>::operator=(std::move(v));
      std::cout << "Vector& Vector::operator=(Vector&&)\n";
    }
    return *this;
  }
  Vector(Vector&& v) : std::vector<int>(std::move(v)) {
     std::cout << "Vector(Vector&&)\n";
  }
};

void copy_accumulate(Vector &v) {
    Vector v2 = std::accumulate(v.begin(), v.end(), Vector(), 
                    [](Vector & v, int i) {
                      v.push_back(i);
                      return v;
                    });
    std::cout << "size of v2 = " << v2.size() << "\n";
}

void nocopy_accumulate(Vector &v) {
    Vector init;
    Vector v2 = std::accumulate(v.begin(), v.end(), std::ref(init), 
                    [](std::reference_wrapper<Vector> v, int i) {
                      v.get().push_back(i);
                      return v;
                    });
    std::cout << "size of v2 = " << v2.size() << "\n";
}

int main()
{
    Vector v { 1, 2, 3, 4 };
    copy_accumulate(v);
    std::cout << "=============\n";
    nocopy_accumulate(v);
}

Comments

MR said…
try this one
void copy_accumulate(Vector &v) {

Vector v2 = std::accumulate(v.begin(), v.end(), Vector(),
[](Vector & v, int i)->Vector& {
v.push_back(i);
return v;
});
std::cout << "size of v2 = " << v2.size() << "\n";
}
Anonymous said…
Because std::accumulate is intended for numeric accumulators, so copy worth nothing with them.
Anonymous said…
void copy_accumulate(Vector& v) {
Vector v2 = std::accumulate(v.begin(), v.end(), Vector(),
[] (Vector& v, int i) {
v.push_back(i);
return std::ref(v);
});
std::cout << "size of v2 = " << v2.size() << "\n";
}
Sumant said…
Good alternative solutions. Thanks folks!

Popular Content

Multi-dimensional arrays in C++11

What new can be said about multi-dimensional arrays in C++? As it turns out, quite a bit! With the advent of C++11, we get new standard library class std::array. We also get new language features, such as template aliases and variadic templates. So I'll talk about interesting ways in which they come together. It all started with a simple question of how to define a multi-dimensional std::array. It is a great example of deceptively simple things. Are the following the two arrays identical except that one is native and the other one is std::array? int native[3][4]; std::array<std::array<int, 3>, 4> arr; No! They are not. In fact, arr is more like an int[4][3]. Note the difference in the array subscripts. The native array is an array of 3 elements where every element is itself an array of 4 integers. 3 rows and 4 columns. If you want a std::array with the same layout, what you really need is: std::array<std::array<int, 4>, 3> arr; That's quite annoying for...

Review of Manning's Functional Programming in C++

Last year I reviewed the pre-print manuscript of Manning's Functional Programming in C++ written by Ivan Čukić. I really enjoyed reading the book. I enthusiastically support that the book Offers precise, easy-to-understand, and engaging explanations of functional concepts. Who is this book for This book expects a reasonable working knowledge of C++, its modern syntax, and semantics from the readers. Therefore, reading this book might require a companion book for C++ beginners. I think that’s fair because FP is an advanced topic. C++ is getting more and more powerful day by day. While there are many FP topics that could be discussed in such a book, I like the practicality of the topics selected in this book. Here's the table of contents at a glance. This is a solid coverage of functional programming concepts to get a determined programmer going from zero-to-sixty in a matter of weeks. Others have shared their thoughts on this book as well. See Rangarajan Krishnamo...

Want speed? Use constexpr meta-programming!

It's official: C++11 has two meta-programming languages embedded in it! One is based on templates and other one using constexpr . Templates have been extensively used for meta-programming in C++03. C++11 now gives you one more option of writing compile-time meta-programs using constexpr . The capabilities differ, however. The meta-programming language that uses templates was discovered accidently and since then countless techniques have been developed. It is a pure functional language which allows you to manipulate compile-time integral literals and types but not floating point literals. Most people find the syntax of template meta-programming quite abominable because meta-functions must be implemented as structures and nested typedefs. Compile-time performance is also a pain point for this language feature. The generalized constant expressions (constexpr for short) feature allows C++11 compiler to peek into the implementation of a function (even classes) and perform optimization...