GCC Code Coverage Report


Directory: src/
File: src/algo/vecops.c
Date: 2026-09-30 11:11:31
Exec Total Coverage
Lines: 105 105 100.0%
Functions: 20 20 100.0%
Branches: 36 36 100.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2026 Tiger Data, Inc.
3 * Licensed under the PostgreSQL License. See LICENSE for details.
4 *
5 * vecops.c - General vector operations with SIMD dispatch
6 *
7 * Provides compiler-vectorized implementations with target_clones for
8 * automatic ISA selection. Hand-optimized SIMD implementations can be
9 * added in vecops_avx2.c, vecops_avx512.c, vecops_neon.c if needed.
10 */
11
12 #include "vs_config.h"
13
14 #include <math.h>
15 #include <stdatomic.h>
16 #include <stddef.h>
17 #include <string.h>
18
19 #include "algo/simd_utils.h"
20 #include "algo/vecops.h"
21 #include "core/platform.h"
22
23 /*
24 * Compiler-Vectorized Implementations
25 *
26 * These use target_clones to generate multiple versions for different ISAs.
27 * The dynamic linker selects the best version at load time.
28 */
29
30 VS_TARGET_CLONES static float
31 34526928 vecops_dot_product_compiler(const float *a, const float *b, Dimension dim)
32 {
33 34526928 float sum = 0.0f;
34
2/2
✓ Branch 0 taken 1373467900 times.
✓ Branch 1 taken 34526928 times.
1407994828 for (Dimension i = 0; i < dim; i++)
35 1373467900 sum += a[i] * b[i];
36 34526928 return sum;
37 }
38
39 VS_TARGET_CLONES static float
40 33828696 vecops_l2_norm_squared_compiler(const float *v, Dimension dim)
41 {
42 33828696 float sum = 0.0f;
43
2/2
✓ Branch 0 taken 1393265218 times.
✓ Branch 1 taken 33828696 times.
1427093914 for (Dimension i = 0; i < dim; i++)
44 1393265218 sum += v[i] * v[i];
45 33828696 return sum;
46 }
47
48 VS_TARGET_CLONES static float
49 620770 vecops_vector_sum_compiler(const float *v, Dimension dim)
50 {
51 620770 float sum = 0.0f;
52
2/2
✓ Branch 0 taken 36820627 times.
✓ Branch 1 taken 620770 times.
37441397 for (Dimension i = 0; i < dim; i++)
53 36820627 sum += v[i];
54 620770 return sum;
55 }
56
57 VS_TARGET_CLONES static void
58 716656 vecops_vector_sub_compiler(
59 const float *a, const float *b, float *out, Dimension dim)
60 {
61
2/2
✓ Branch 0 taken 43268977 times.
✓ Branch 1 taken 716656 times.
43985633 for (Dimension i = 0; i < dim; i++)
62 43268977 out[i] = a[i] - b[i];
63 716656 }
64
65 VS_TARGET_CLONES static void
66 695 vecops_vector_add_compiler(
67 const float *a, const float *b, float *out, Dimension dim)
68 {
69
2/2
✓ Branch 0 taken 62483 times.
✓ Branch 1 taken 695 times.
63178 for (Dimension i = 0; i < dim; i++)
70 62483 out[i] = a[i] + b[i];
71 695 }
72
73 VS_TARGET_CLONES static void
74 37092 vecops_vector_scale_compiler(
75 const float *v, float scalar, float *out, Dimension dim)
76 {
77
2/2
✓ Branch 0 taken 318691 times.
✓ Branch 1 taken 37092 times.
355783 for (Dimension i = 0; i < dim; i++)
78 318691 out[i] = v[i] * scalar;
79 37092 }
80
81 VS_TARGET_CLONES static float
82 19900967 vecops_l2_distance_squared_compiler(
83 const float *a, const float *b, Dimension dim)
84 {
85 19900967 float sum = 0.0f;
86
2/2
✓ Branch 0 taken 1026269726 times.
✓ Branch 1 taken 19900967 times.
1046170693 for (Dimension i = 0; i < dim; i++)
87 {
88 1026269726 float diff = a[i] - b[i];
89 1026269726 sum += diff * diff;
90 }
91 19900967 return sum;
92 }
93
94 /*
95 * Function Pointer Dispatch
96 */
97
98 typedef float (*DotProductFn)(const float *, const float *, Dimension);
99 typedef float (*NormFn)(const float *, Dimension);
100 typedef float (*SumFn)(const float *, Dimension);
101 typedef void (*BinaryOpFn)(const float *, const float *, float *, Dimension);
102 typedef void (*ScaleFn)(const float *, float, float *, Dimension);
103 typedef float (*DistanceFn)(const float *, const float *, Dimension);
104
105 static DotProductFn g_dot_product_fn = NULL;
106 static NormFn g_l2_norm_squared_fn = NULL;
107 static SumFn g_vector_sum_fn = NULL;
108 static BinaryOpFn g_vector_sub_fn = NULL;
109 static BinaryOpFn g_vector_add_fn = NULL;
110 static ScaleFn g_vector_scale_fn = NULL;
111 static DistanceFn g_l2_distance_squared_fn = NULL;
112
113 static const char *g_impl_name = NULL;
114 static _Atomic(bool) g_initialized = false;
115
116 void
117 16 vs_vecops_force_reinit(void)
118 {
119 16 g_initialized = false;
120 16 g_dot_product_fn = NULL;
121 16 g_l2_norm_squared_fn = NULL;
122 16 g_vector_sum_fn = NULL;
123 16 g_vector_sub_fn = NULL;
124 16 g_vector_add_fn = NULL;
125 16 g_vector_scale_fn = NULL;
126 16 g_l2_distance_squared_fn = NULL;
127 16 g_impl_name = NULL;
128 16 }
129
130 int
131 212 vs_vecops_init(void)
132 {
133
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 30 times.
212 if (g_initialized)
134 6 return 0;
135
136 /*
137 * For now, use compiler-vectorized implementations for all operations.
138 * Hand-optimized SIMD can be added later if profiling shows benefit.
139 *
140 * The compiler does a good job with these simple loops, especially
141 * with target_clones generating AVX2/AVX-512 versions automatically.
142 */
143 206 g_dot_product_fn = vecops_dot_product_compiler;
144 206 g_l2_norm_squared_fn = vecops_l2_norm_squared_compiler;
145 206 g_vector_sum_fn = vecops_vector_sum_compiler;
146 206 g_vector_sub_fn = vecops_vector_sub_compiler;
147 206 g_vector_add_fn = vecops_vector_add_compiler;
148 206 g_vector_scale_fn = vecops_vector_scale_compiler;
149 206 g_l2_distance_squared_fn = vecops_l2_distance_squared_compiler;
150 206 g_impl_name = "compiler";
151
152 206 g_initialized = true;
153 206 return 0;
154 }
155
156 const char *
157 4 vs_vecops_impl_name(void)
158 {
159
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (vs_unlikely(!g_initialized))
160 2 vs_vecops_init();
161 4 return g_impl_name;
162 }
163
164 /*
165 * Public API
166 */
167
168 float
169 34526928 vs_dot_product(const float *a, const float *b, Dimension dim)
170 {
171
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 34526925 times.
34526928 if (vs_unlikely(!g_initialized))
172 3 vs_vecops_init();
173 34526928 return g_dot_product_fn(a, b, dim);
174 }
175
176 float
177 33828696 vs_l2_norm_squared(const float *v, Dimension dim)
178 {
179
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 33828579 times.
33828696 if (vs_unlikely(!g_initialized))
180 117 vs_vecops_init();
181 33828696 return g_l2_norm_squared_fn(v, dim);
182 }
183
184 float
185 24452 vs_l2_norm(const float *v, Dimension dim)
186 {
187 24452 return sqrtf(vs_l2_norm_squared(v, dim));
188 }
189
190 float
191 620770 vec32_sum(const float *v, Dimension dim)
192 {
193
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 620768 times.
620770 if (vs_unlikely(!g_initialized))
194 2 vs_vecops_init();
195 620770 return g_vector_sum_fn(v, dim);
196 }
197
198 void
199 716656 vec32_sub(const float *a, const float *b, float *out, Dimension dim)
200 {
201
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 716590 times.
716656 if (vs_unlikely(!g_initialized))
202 66 vs_vecops_init();
203 716656 g_vector_sub_fn(a, b, out, dim);
204 716656 }
205
206 void
207 695 vec32_add(const float *a, const float *b, float *out, Dimension dim)
208 {
209
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 693 times.
695 if (vs_unlikely(!g_initialized))
210 2 vs_vecops_init();
211 695 g_vector_add_fn(a, b, out, dim);
212 695 }
213
214 void
215 37092 vec32_scale(const float *v, float scalar, float *out, Dimension dim)
216 {
217
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 37090 times.
37092 if (vs_unlikely(!g_initialized))
218 2 vs_vecops_init();
219 37092 g_vector_scale_fn(v, scalar, out, dim);
220 37092 }
221
222 void
223 90 vec32_mean(const float *vectors, uint32_t nvecs, Dimension dim, float *out)
224 {
225 90 memset(out, 0, dim * sizeof(float));
226
2/2
✓ Branch 0 taken 687 times.
✓ Branch 1 taken 90 times.
777 for (uint32_t i = 0; i < nvecs; i++)
227 687 vec32_add(out, vectors + (size_t)i * dim, out, dim);
228 90 vec32_scale(out, 1.0f / (float)nvecs, out, dim);
229 90 }
230
231 void
232 14 vs_global_mean(
233 const float *centroids,
234 uint32_t ncentroids,
235 Dimension dim,
236 DistanceMetric metric,
237 float *out)
238 {
239 14 vec32_mean(centroids, ncentroids, dim, out);
240
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (metric == DISTANCE_COSINE)
241 2 vs_l2_normalize(out, dim);
242 14 }
243
244 float
245 19900967 vs_l2_distance_squared(const float *a, const float *b, Dimension dim)
246 {
247
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 19900955 times.
19900967 if (vs_unlikely(!g_initialized))
248 12 vs_vecops_init();
249 19900967 return g_l2_distance_squared_fn(a, b, dim);
250 }
251