# valarray vs. vector – numeriska beräkningar

23 augusti 2025 | https://www.ribomation.se/blog/2025/valarray-vs.-vector-numeriska-berakningar/

När den första C++-standarden (C++98) formades, var en av ambitionerna att språket skulle innehålla byggblock för **numerisk programmering**. Vid den tiden var kompilatorernas autovektorisering betydligt svagare än idag, och man hoppades att ett specialiserat API skulle göra det enklare för både programmerare och kompilatorer att uttrycka och optimera matematiska operationer på hela arrayer.

Resultatet blev `std::valarray`: en container med fokus på **elementvisa operationer**, **matematiska funktioner** och möjligheten att arbeta på **delmängder via slicing och maskning**. Tanken var att man skulle kunna skriva C++-kod som såg ut som algebra – utan loopar och med potential för högeffektiv kodgenerering.

I praktiken blev `valarray` dock aldrig riktigt populär. Dels för att den inte fungerar som en ”riktig” STL-container (inga iteratorer, begränsad interoperabilitet), dels för att kompilatorerna snabbt blev mycket bättre på att optimera vanliga loopar över `std::vector`. Sedan dess har `valarray` förblivit något av en doldis – men det betyder inte att den är värdelös.

I den här artikeln tittar vi närmare på likheter, skillnader och när `valarray` faktiskt kan vara ett vettigt val.

Likheter
--------

*   Båda lagrar homogena element (`T`) i en sekvens.
*   Båda stöder slumpmässig indexering med `operator[]`.
*   Båda kan ändra storlek dynamiskt.
*   Båda kan användas för numeriska beräkningar.

Skillnader
----------

### Designmål

*   **`std::vector`** är en allmän sekvenscontainer med fullt STL-stöd: iteratorer, algoritmer, allocatorer och garanterat sammanhängande minne.
*   **`std::valarray`** är specialiserad för **numerik och elementvisa operationer**, med operatorer som fungerar på hela arrayer samtidigt.

### Interoperabilitet

*   `vector` är fullt integrerad i STL och fungerar tillsammans med alla STL algoritmer.
*   `valarray` saknar iteratorer och fungerar inte sömlöst med `<algorithm>`.

### Funktionalitet

*   `vector` har ett rikt API för insättning, radering, kapacitetshantering osv.
*   `valarray` erbjuder istället numeriska operationer: elementvis aritmetik, reduktionsfunktioner (`sum`, `min`, `max`), funktionsapplicering (`apply`), samt slicing och maskning.

### Minnesmodell

*   `vector` garanterar sammanhängande minne (bra för interoperabilitet med C-APIs och SIMD).
*   `valarray` har historiskt haft frihet att använda andra representationer för att underlätta optimering (i praktiken ligger de flesta ändå i ett minnesblock).

När använda `std::valarray`?
----------------------------

*   När du vill skriva **kort och tydlig matematik** i koden.
*   När slicing eller maskning förenklar hanteringen av delmängder.
*   När du snabbt vill utföra elementvisa operationer utan att bygga loopar eller anropa STL-algoritmer.

I alla andra fall – särskilt om du behöver interoperabilitet, iteratorer, eller integration med algoritmer – är `std::vector` det naturliga valet.

* * *

Kodexempel
----------

### 1\. Normalisering

Med `valarray`:

```cpp
#include <valarray>
#include <cmath>

std::valarray<double> x = {1, 2, 3, 4};

double mean = x.sum() / x.size();
double var  = ((x - mean) * (x - mean)).sum() / x.size();
double sd   = std::sqrt(var);

x = (x - mean) / sd; // z-normalisering
```

Med `vector`:

```cpp
#include <vector>
#include <numeric>
#include <algorithm>
#include <cmath>

std::vector<double> x {1,2,3,4};

double mean = std::accumulate(x.begin(), x.end(), 0.0) / x.size();

double sqsum = 0.0;
std::for_each(x.begin(), x.end(), [&](double v){ sqsum += (v-mean)*(v-mean); });
double sd = std::sqrt(sqsum / x.size());

std::transform(x.begin(), x.end(), x.begin(), [&](double v){ return (v - mean) / sd; });
```

### 2\. Selektiv tilldelning

Med `valarray`:

```cpp
std::valarray<double> v = {-2.0, 0.5, 3.0, -1.0, 2.2};
std::valarray<bool> mask = v > 0.0;
v[mask] = 1.0; // endast positiva ersätts
```

Med `vector`:

```cpp
std::vector<double> v {-2.0, 0.5, 3.0, -1.0, 2.2};
for (auto& x : v)
if (x > 0.0) x = 1.0;
```

### 3\. Kolumnoperation med slice

Med `valarray`:

```cpp
#include <valarray>
#include <cstddef>

std::size_t rows = 3, cols = 4;
std::valarray<double> M(rows * cols);
// ... fyll M ...

std::size_t j = 2;
std::slice col_j(j, rows, cols);

double mean = M[col_j].sum() / rows;
M[col_j] -= mean; // normalisera kolumnen
```

Med `vector` + manuell indexering:

```cpp
std::vector<double> M(rows * cols);
// ... fyll M ...

std::size_t j = 2;
double sum = 0.0;
for (std::size_t r = 0; r < rows; ++r)
sum += M[r*cols + j];
double mean = sum / rows;

for (std::size_t r = 0; r < rows; ++r)
M[r*cols + j] -= mean;
```

Benchmark: vector vs valarray
-----------------------------

Ett vanligt påstående är att valarray ska vara snabbare för numeriska operationer. Men stämmer det idag? Låt oss testa med Google Benchmark. Vi beräknar helt enkelt summan av kvadraterna för en vektor på 1 miljon element.

```cpp
#include <benchmark/benchmark.h>
#include <vector>
#include <valarray>
#include <numeric>
#include <random>

static void BM_Vector_SumSquares(benchmark::State& state) {
    std::vector<double> v(state.range(0));
    std::iota(v.begin(), v.end(), 1.0);
    for (auto _ : state) {
        double sum = 0.0;
        for (double x : v) sum += x*x;
        benchmark::DoNotOptimize(sum);
    }
}
BENCHMARK(BM_Vector_SumSquares)->Arg(1'000'000);

static void BM_Valarray_SumSquares(benchmark::State& state) {
    std::valarray<double> v(state.range(0));
    for (std::size_t i=0; i<v.size(); ++i) v[i] = i+1.0;
    for (auto _ : state) {
        double sum = (v * v).sum();
        benchmark::DoNotOptimize(sum);
    }
}
BENCHMARK(BM_Valarray_SumSquares)->Arg(1'000'000);

BENCHMARK_MAIN();
```

### Typiska resultat (Clang/GCC med -O3, Linux, x86-64)

Container

Tid per iteration

vector

ca 2.8 ms

valarray

ca 2.9 ms

### Aktuella resultat, i Compiler Explorer

```text
Run on (2 X 3190.45 MHz CPU s)
Load Average: 0.30, 0.29, 0.30
-------------------------------------------------------------------------
Benchmark                               Time             CPU   Iterations
-------------------------------------------------------------------------
BM_Vector_SumSquares/1000000         1.892 ms      1.286 ms          543
BM_Valarray_SumSquares/1000000       2.212 ms      1.291 ms          543
```

 [![Compiler Explorer](/images/compiler-explorer-logo.svg "Compiler Explorer - Try it yourself") Try it yourself on Compiler Explorer](https://godbolt.org/z/qTrss98Ma)

#### Vad betyder kolumnresultaten?

*   **Time**: Den faktiska klocktid som gick under testet.
*   **CPU**: Den beräknade CPU-tiden per iteration. Denna är ofta den mest intressanta för jämförelser eftersom den är mindre känslig för brus (OS-schemaläggning, bakgrundslaster).
*   **Iterations**: Antalet gånger benchmarket kördes för att få stabila siffror.

#### Analys

**1\. Vector vs Valarray:**

*   vector: 1.892 ms
*   valarray: 2.212 ms → Här ser vi att valarray tar ungefär 17 % längre tid i väggtid (_wall-clock time_).

**2\. Vector vs Valarray (CPU-tid):**

*   vector: 1.286 ms
*   valarray: 1.291 ms → Här är skillnaden praktiskt taget obefintlig. Vi pratar om 0.4 % skillnad, vilket ligger helt inom mätbruset.

**3\. Tolkning:**

*   Både vector och valarray kompileras till i princip lika effektiva loopar.
*   Att väggtiden är lite högre för valarray kan bero på små skillnader i hur uttrycket (v \* v).sum() översätts (tillfälliga objekt, cache-effekter etc.), men det är inte konsekvent.
*   CPU-tiden visar att i praktiken presterar de två lösningarna lika.

Benchmarket visar att det inte finns någon praktisk prestandafördel med std::valarray jämfört med std::vector i moderna kompilatorer. Eventuella små skillnader beror på implementation och är försumbara i verkliga program.

Det innebär att valet mellan vector och valarray bör göras utifrån kodens tydlighet och behov snarare än rå prestanda.

Praktiska riktlinjer
--------------------

*   ✅ Använd `std::vector` som standardval: den är den mest mångsidiga och bäst integrerade containern i STL.
    
*   ✅ Välj `std::valarray` när:
    
    *   du vill skriva kod som ser ut som matematik,
    *   du behöver slicing eller maskning på arrayer,
    *   du prioriterar kompakt och deklarativ kod framför maximal interoperabilitet.
*   ❌ Använd inte valarray om du behöver iteratorer, STL-algoritmer, eller interoperabilitet med externa bibliotek/C-API.
    
*   💡 För moderna flerdimensionella arrayer kan `std::mdspan` (C++23) vara ett ännu bättre alternativ. _PS, mdspan finns ännu inte implementerad_.
    

Slutsats
--------

`std::vector` är alltid förstahandsvalet för generell programmering, integration med STL och arbete nära hårdvaran.

`std::valarray` är en doldis som fortfarande kan skina i situationer där matematiska uttryck, slicing och maskning gör koden mer deklarativ och lättläst.

Även om prestandafördelen i praktiken ofta är liten jämfört med moderna loopar över vector, så kan valarray ibland ge en mer elegant lösning på numeriska problem. Kanske dags att damma av denna bortglömda del av STL?
