
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
:
#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
:
#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
:
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
:
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
:
#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:
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.
#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
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
Try it yourself on Compiler Explorer
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?
Länkar
Senaste Artiklarna
valarray vs. vector – två olika vägar för numeriska beräkningar
23 augusti 2025Visste du att string kan användas som en vector
19 augusti 2025Så här använder du span-view i C++
17 augusti 2025Så här använder du adapter-containrar i C++
16 augusti 2025Bekanta dig med nya flat containers i C++
14 augusti 2025