compute-acceleration
GEMM and sum reduction across serial, OpenMP and CUDA
Loading...
Searching...
No Matches
cpu_gemm.h
1// Copyright (c) 2025 yanghuafang
2// SPDX-License-Identifier: MIT
3
4#ifndef ACCEL_CPU_CPU_GEMM_H_
5#define ACCEL_CPU_CPU_GEMM_H_
6
7#include "core/gemm_shape.h"
8#include "core/span.h"
9
10namespace accel {
11
12// 64 floats per tile edge is a 16 KiB block, so a blocked update keeps a
13// ~48 KiB working set live. Whether that fits is microarchitecture-dependent:
14// blocking buys 2.6x on an Apple M5 and 1.4x on a 7950X. Tune with --tile=N.
15inline constexpr int kDefaultTileSize = 64;
16
17// All five compute the accumulating update C += A * B (BLAS beta = 1), so a
18// caller wanting a plain product must zero C first. Index letters give the
19// loop nesting order: i walks M, j walks N, k the contraction.
20//
21// Extents are validated once before the loop nest, which is what lets the
22// inner loops index unchecked; a violation throws std::invalid_argument.
23// Requires shape.is_valid(), spans sized to match, and c not aliasing a or b.
24//
25// Single-threaded by design: these isolate memory access order. Parallelism is
26// a separate axis, in omp/gemm_omp.h.
27
28// Baseline. The inner loop strides B by n floats, so all but one value of each
29// fetched cache line is evicted before use.
30void GemmIjk(Span<const float> a, Span<const float> b, Span<float> c,
31 const GemmShape& shape);
32
33// Hoisting the contraction out of the N loop makes the inner statement a
34// scalar-times-row AXPY over unit-stride B and C, which auto-vectorises. C is
35// then read and written every iteration rather than held in a register -- a
36// trade that pays for itself many times over.
37void GemmIkj(Span<const float> a, Span<const float> b, Span<float> c,
38 const GemmShape& shape);
39
40// Cache-blocked. Tail blocks are clamped, so extents need not divide
41// tile_size; a non-positive tile_size throws std::invalid_argument.
42void GemmTiled(Span<const float> a, Span<const float> b, Span<float> c,
43 const GemmShape& shape, int tile_size = kDefaultTileSize);
44
45// Expects B transposed, b_col[j * k + l] == B(l, j), via RowToColumnMajor().
46// Both inner-loop operands are then unit-stride: a pair of dot products.
47void GemmIjkBColMajor(Span<const float> a, Span<const float> b_col,
48 Span<float> c, const GemmShape& shape);
49
50// Blocking plus the transposed layout. The two target the same bottleneck
51// instead of compounding: faster than the unblocked column-major kernel on an
52// M5, slower on a 7950X, and beaten by GemmIkj everywhere. See
53// docs/Benchmarks.md.
54void GemmTiledBColMajor(Span<const float> a, Span<const float> b_col,
55 Span<float> c, const GemmShape& shape,
56 int tile_size = kDefaultTileSize);
57
58} // namespace accel
59
60#endif // ACCEL_CPU_CPU_GEMM_H_