nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
kernels.hpp
1// nk/ops/kernels.hpp
2//
3// Public raw-buffer compute kernels — the STABLE facade over the backend-split
4// inner loops (which live in nk::ops::detail and are selected at build time
5// by NK_WITH_SIMD). Two audiences:
6//
7// * the codegen subsystem — generated C++ calls these directly as its
8// lowering target for heavy array ops (column-major double buffers, the
9// same RawBuffer ABI the emitter already uses), so a transpiled kernel
10// reuses ops' SIMD implementation instead of emitting a naive loop; and
11// * any in-tree caller that already holds raw buffers and wants the fast path
12// without going through Value.
13//
14// Callers use ONLY this header — never nk::ops::detail. This is where the
15// complex / elementwise / transcendental / fft kernel entries will also land
16// as the codegen lowering surface grows.
17#pragma once
18
19#include <complex>
20#include <cstddef>
21
22// Linkage. Default (static — compiled into nk_ops, a test, or a
23// self-contained artifact): plain. Building the nk_ops_kernels shared lib:
24// dllexport. A consumer linking that shared lib (a codegen artifact that uses
25// ops kernels): define NK_OPS_USE_DLL -> dllimport. No effect off Windows.
26// Mirrors NK_RT_API (nk_codegen_rt.h).
27#if defined(_WIN32) && defined(NK_OPS_BUILDING_DLL)
28# define NK_OPS_API __declspec(dllexport)
29#elif defined(_WIN32) && defined(NK_OPS_USE_DLL)
30# define NK_OPS_API __declspec(dllimport)
31#else
32# define NK_OPS_API
33#endif
34
35namespace nk::ops {
36
37// C(M×N) = A(M×K) · B(K×N), all column-major (a[k*M+i], b[j*K+k], c[j*M+i]).
38// C is caller-allocated; the kernel zeroes then accumulates. SIMD MulAdd down
39// columns of A under NK_WITH_SIMD, a portable loop otherwise.
40NK_OPS_API void matmulDouble(const double *a, const double *b, double *c,
41 std::size_t M, std::size_t N, std::size_t K);
42
43// Same, for complex matrices (column-major std::complex<double>). Portable
44// SAXPY loop (no SIMD-complex split); the single call is amortised over O(M·N·K)
45// work, so it carries no per-element overhead vs an inline loop.
46NK_OPS_API void matmulComplex(const std::complex<double> *a, const std::complex<double> *b,
47 std::complex<double> *c,
48 std::size_t M, std::size_t N, std::size_t K);
49
50// Element-wise binary ops over two equal-length DOUBLE buffers: out[i] = a[i] OP
51// b[i], n elements (flat — rank-agnostic). SIMD under NK_WITH_SIMD with an
52// internal small-N scalar gate (so no dynamic-dispatch crater at tiny n). NOTE:
53// a plain __restrict inline loop already auto-vectorises to match these for
54// cheap arithmetic (A3) — codegen uses them only as the opt-in ops-kernel tier;
55// the inline loop stays the self-contained default.
56NK_OPS_API void plusDouble (const double *a, const double *b, double *out, std::size_t n);
57NK_OPS_API void minusDouble (const double *a, const double *b, double *out, std::size_t n);
58NK_OPS_API void timesDouble (const double *a, const double *b, double *out, std::size_t n);
59NK_OPS_API void rdivideDouble(const double *a, const double *b, double *out, std::size_t n);
60
61// Element-wise transcendentals over a DOUBLE buffer: out[i] = fn(x[i]), n
62// elements. Forward to the SIMD fusedTransAffine kernel (scale=1, offset=0) —
63// the SAME vectorised path the runtime's expression fusion uses, so the result
64// matches the interpreter. A plain std::<fn> inline loop does NOT auto-vectorise
65// on MSVC, so these are a genuine speedup (unlike the cheap-arithmetic ops). The
66// set is the real-total transcendentals (no complex-domain decline): the
67// codegen lowers exactly these inline today and routes them here when opt-in.
68NK_OPS_API void sinDouble (const double *x, double *out, std::size_t n);
69NK_OPS_API void cosDouble (const double *x, double *out, std::size_t n);
70NK_OPS_API void tanDouble (const double *x, double *out, std::size_t n);
71NK_OPS_API void atanDouble (const double *x, double *out, std::size_t n);
72NK_OPS_API void sinhDouble (const double *x, double *out, std::size_t n);
73NK_OPS_API void coshDouble (const double *x, double *out, std::size_t n);
74NK_OPS_API void tanhDouble (const double *x, double *out, std::size_t n);
75NK_OPS_API void expDouble (const double *x, double *out, std::size_t n);
76NK_OPS_API void asinhDouble(const double *x, double *out, std::size_t n);
77NK_OPS_API void expm1Double(const double *x, double *out, std::size_t n);
78
79} // namespace nk::ops