1

I'm trying to compare the performance of using std::valarray vs. std::vector/std::transform operations using Google Bench. I'm using QuickBench.

My code (for QuickBench) is

#include <random>

class RG {
public:
    double operator()() noexcept { return dis(gen); } 
private:
    std::mt19937 gen {std::random_device{}()};
    std::uniform_real_distribution<> dis {0.0, 100.0};
};

static RG rg {};

static constexpr auto N = 1000;

#include <vector>
#include <algorithm>

static void Vector(benchmark::State& state) {
    std::vector<double> v1, v2;
    v1.reserve(N); v2.reserve(N);
    std::generate_n(back_inserter(v1),N,rg);
    std::generate_n(back_inserter(v2),N,rg);
    for (auto _ : state) {
        std::vector<double> v3; v3.reserve(N);
        std::transform(cbegin(v1), cend(v1), cbegin(v2), back_inserter(v3),
            [](auto d1, auto d2) noexcept { return 0.5*d1+1.5*d2;});
        benchmark::DoNotOptimize(v3);
    }
}
// Register the function as a benchmark
BENCHMARK(Vector);

#include <valarray>

static void ValArray(benchmark::State& state) {
    std::valarray<double> v1{N}, v2{N};
    std::generate(begin(v1),end(v1),rg);
    std::generate(begin(v2),end(v2),rg);
    for (auto _ : state) {
        std::valarray<double> v3{};
        v3=0.5*v1+1.5*v2;
        benchmark::DoNotOptimize(v3);
    }
}
BENCHMARK(ValArray);

QuickBench link Quickbench says the std::valarray is 72 times faster then std::vector. That can't be right, right? What am I doing wrong?

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • 1
    The problem with `std::vector` is that it either forces a zero-initialization on you (resize) or lots of branches constantly checking if new allocation is needed (reserve + back_inserter). `unique_ptr` gives similar perf to `valarray`: https://quick-bench.com/q/-aLzQp5kI0AfjU4rj7teRA_Pk5g – Aykhan Hagverdili Feb 28 '21 at 15:25
  • Here's one with 500,000 elements and `par_unseq` versions: https://quick-bench.com/q/hF3uylCLK5lMWnj0hZLVTjN23j4 – Aykhan Hagverdili Feb 28 '21 at 15:55
  • @AyxanHaqverdili thanks. I just wanted to know if valarray was any performant. I never see it used in any code. An I found some post about it, calling it "broken". (In my experience part of it were actually broken [and just recently fixed](https://github.com/microsoft/STL/commit/0e7b5d2509b96f7880bf69417264328df0d53c58#diff-7adde92f40f18f0ab2ae0e1f5828ea180f7a0ff83acd140990d3a65b818d6339). But benching it I found it performs pretty good! – JHBonarius Feb 28 '21 at 19:20

1 Answers1

3

Line 36:

std::valarray<double> v1{N}, v2{N};

This creates two one-element valarray-s, that is, each containing a single element whose value is N=1000. This is because the list-initialization syntax, {}, uses the constructor taking std::initializer_list:

valarray(std::initializer_list<T> il);

which constructs a valarray with the contents of that list, as opposed to the intended one:

explicit valarray(std::size_t count);

that would construct a valarray of count elements.

You need to change that line to:

std::valarray<double> v1(N), v2(N);
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160