some more work

This commit is contained in:
antonl 2026-03-14 21:45:42 +01:00
parent 5ee722a96b
commit f81c97bb1e

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -11,9 +12,29 @@ import (
"github.com/james-bowman/sparse" "github.com/james-bowman/sparse"
) )
type SparseMatrixTiming struct {
Label string `json:"label"`
Rows int `json:"rows"`
Cols int `json:"cols"`
NNZ int `json:"nnz"`
MatMulRuns int `json:"matmul_runs"`
MatMulTotalNs int64 `json:"matmul_total_ns"`
MatMulAvgNs int64 `json:"matmul_avg_ns"`
SolveRuns int `json:"solve_runs"`
SolveTotalNs int64 `json:"solve_total_ns"`
SolveAvgNs int64 `json:"solve_avg_ns"`
}
type SparseBenchmarkCase struct {
Timing SparseMatrixTiming
Matrix *sparse.CSR
}
// The "SuiteSparse" collection of matrices come in a format called // The "SuiteSparse" collection of matrices come in a format called
// "market matrix" and so we parse and load them to a sparse.COO format here. // "market matrix" and so we parse and load them to a sparse.COO format here.
func matrix_load_market(path string) *sparse.COO { func matrixLoadMarket(path string) *sparse.CSR {
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
panic(err) panic(err)
@ -60,47 +81,100 @@ func matrix_load_market(path string) *sparse.COO {
coo := sparse.NewCOO(rows, cols, r, c, v) coo := sparse.NewCOO(rows, cols, r, c, v)
return coo return coo.ToCSR()
} }
func main() { // small helper just to strip path and file ending
// small_test_matrix_path := "suitesparse_test_matrices/1138_bus.mtx" func matrixGetLabel(path string) string {
big_8mill_nnz_matrix_path := "suitesparse_test_matrices/thermal2.mtx" base := filepath.Base(path) // f. ex "1138_bus.mtx"
m := matrix_load_market(big_8mill_nnz_matrix_path) ext := filepath.Ext(base) // ".mtx"
rows, cols := m.Dims() out := strings.TrimSuffix(base, ext) // just "1138_bus"
fmt.Printf("matrix size: %d x %d \n", rows, cols) return out
}
func getSparseBenchmarkCase(path string) SparseBenchmarkCase {
var out SparseBenchmarkCase
loadMarketStart := time.Now()
label := matrixGetLabel(path)
m := matrixLoadMarket(path)
loadMarketEnd := time.Since(loadMarketStart)
loadedMarketMillisecs := float64(loadMarketEnd.Milliseconds())
fmt.Printf("Loaded market matrix %s in %.4f ms \n", label, loadedMarketMillisecs)
out.Matrix = m
out.Timing.Label = label
rows, cols := out.Matrix.Dims()
nnz := m.NNZ()
out.Timing.Rows = rows
out.Timing.Cols = cols
out.Timing.NNZ = nnz
return out
}
func timeSparseMatmuls(bcase *SparseBenchmarkCase) {
rows := bcase.Timing.Rows
cols := bcase.Timing.Cols
if rows != cols { if rows != cols {
panic("sparse matmul benchmark assumes square matrix") panic("sparse matmul benchmark assumes square matrix")
} }
// Convert to Compressed-Sparse-Row-storage fmt.Printf("Timing sparse matrix %s with size: %d x %d \n", bcase.Timing.Label, rows, cols)
m_csr := m.ToCSR()
// Warm up caches to not include "first call" effects mCSR := bcase.Matrix
var warm sparse.CSR var warm sparse.CSR
for i := 0; i < 3; i += 1 { for i := 0; i < 3; i += 1 {
warm.Mul(m_csr, m_csr) warm.Mul(mCSR, mCSR)
} }
// The CSR x CSR matrix multiplication is supposed to have a specific optimised // The CSR x CSR matrix multiplication is supposed to have a specific optimised
// path for matmuls // path for matmuls
var out sparse.CSR var out sparse.CSR
number_of_mults := 100 numberOfMults := 100
time_begin := time.Now() fmt.Printf("NNZ before matmuls: %d\n", mCSR.NNZ())
for i := 0; i < number_of_mults; i += 1 {
out.Mul(m_csr, m_csr) timeBegin := time.Now()
for i := 0; i < numberOfMults; i += 1 {
out.Mul(mCSR, mCSR)
}
timeElapsed := time.Since(timeBegin)
fmt.Printf("NNZ after matmuls: %d\n", out.NNZ())
if warm.NNZ() != out.NNZ() {
panic("Sparsity pattern changed unexpectedly after matmul!")
} }
elapsed := time.Since(time_begin) bcase.Timing.MatMulRuns = numberOfMults
elapsed_ms := elapsed.Milliseconds() bcase.Timing.MatMulTotalNs = timeElapsed.Nanoseconds()
time_per_matmul := float32(elapsed_ms) / float32(number_of_mults) timeAvgNS := timeElapsed.Nanoseconds() / int64(numberOfMults)
bcase.Timing.MatMulAvgNs = timeAvgNS
out_rows, out_cols := out.Dims()
if out_rows != rows || out_cols != cols {
panic("Output matrix dimensions not same as input. We are supposed to matmul square matrices")
} }
fmt.Printf("Elapsed time per mat mul: %.4f ms\n", time_per_matmul) func timeNanoToMS(timeNS int64) float64 {
return float64(timeNS) / float64(1e6)
}
func doTimings(path string) {
bcase := getSparseBenchmarkCase(path)
timeSparseMatmuls(&bcase)
avgMatMulTimeMS := timeNanoToMS(bcase.Timing.MatMulAvgNs)
fmt.Printf("Avg matmul time for %s: %.4f ms \n", bcase.Timing.Label, avgMatMulTimeMS)
}
func main() {
mainBegin := time.Now()
thermal2Path := "suitesparse_test_matrices/thermal2.mtx"
doTimings(thermal2Path)
mainElapsed := time.Since(mainBegin)
fmt.Printf("Program finished in %.4f seconds \n", float64(mainElapsed.Milliseconds())/1e3)
} }