// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package io // The A/B benchmark for the CSV read path: the legacy encoding/csv // reader and the hand-rolled tokenizer side by side, in one process. // The two sub-benchmarks alternate within every -count round, so both // see the same machine, and the decision reads the medians with 1 to 2 // percent treated as noise. import ( "bytes" "io" "testing" "sourcedock.dev/petrbalvin/tensor/internal/core" ) // The A/B table: a hundred thousand rows of sixteen float64 values, in // the text shape SaveCSV emits, sized so the per-value work dominates // everything else. const ( benchCSVABRows = 100000 benchCSVABCols = 16 ) // benchCSVABData renders the A/B table as CSV bytes, once, before the // measured loops. func benchCSVABData(b *testing.B) []byte { b.Helper() var buf bytes.Buffer if err := SaveCSVWriter(&buf, benchFloats(benchCSVABRows, benchCSVABCols)); err != nil { b.Fatal(err) } return buf.Bytes() } // BenchmarkLoadCSVReader races the two readers over the same table. func BenchmarkLoadCSVReader(b *testing.B) { data := benchCSVABData(b) impls := []struct { name string load func(io.Reader, bool) (*core.Array, error) }{ {"old", loadCSVReaderLegacy}, {"new", LoadCSVReader}, } for _, impl := range impls { b.Run(impl.name, func(b *testing.B) { r := bytes.NewReader(data) b.ReportAllocs() for b.Loop() { r.Reset(data) a, err := impl.load(r, false) if err != nil { b.Fatal(err) } if a.Shape()[0] != benchCSVABRows || a.Shape()[1] != benchCSVABCols { b.Fatalf("array came back with shape %v", a.Shape()) } } }) } }