Skip to content

Commit 5b905ea

Browse files
author
peng.li24
committed
refactor(numpycpp): update svml_bridge.h
1 parent c703343 commit 5b905ea

1 file changed

Lines changed: 80 additions & 42 deletions

File tree

numpycpp/detail/svml_bridge.h

Lines changed: 80 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,21 @@ Use #include "numpycpp/numpy.h" instead."
4040
namespace numpy {
4141
namespace detail {
4242
// Internal dispatch namespace — use numpy::exp() etc., not numpy::detail::exp().
43-
// All math functions are resolved at runtime from numpy's _multiarray_umath.so.
4443
//
45-
// 零 static 变量设计: 每次调用 dlsym 直接解析,避免 Python multiprocessing fork
46-
// 后子进程继承父进程的无效 handle 或函数指针。
44+
// fork 安全设计:
45+
// g_svml_gen 是全局代计数器,fork 后自增,所有 resolve_cached<T>() 检测到
46+
// 代变化后自动重新 dlsym。spawn 模式一切从零初始化,无问题。
47+
// static 缓存函数指针避免每元素 dlsym 开销。
48+
//
49+
// find_umath_path() 保持无 static — 每次调用重新扫描 /proc/self/maps。
4750
4851
#include <unistd.h> // getpid
4952
5053
inline void* g_svml_handle = nullptr;
51-
inline pid_t g_svml_pid = 0; // fork 检测: pid 变化则重新初始化
54+
inline pid_t g_svml_pid = 0;
55+
inline int g_svml_gen = 0; // fork 检测代计数器
5256
53-
/// 返回 _multiarray_umath.so 的路径。每次调用重新扫描 /proc/self/maps,
54-
/// 无 static 缓存——fork 安全。
57+
/// 每次调用重新扫描 /proc/self/maps(无 static 缓存)
5558
inline std::string find_umath_path() {
5659
std::ifstream maps("/proc/self/maps");
5760
std::string line;
@@ -71,12 +74,13 @@ inline void bridge_init(const char* numpy_so_path) {
7174
(void)numpy_so_path;
7275
}
7376
74-
/// 解析符号。fork 安全: 如果 pid 变化则重新 dlopen
77+
/// 解析原始符号(无缓存)。fork 检测: pid 变化 → handle 作废 → 重建
7578
inline void* resolve_svml(const char* name) {
7679
pid_t pid = getpid();
7780
if (pid != g_svml_pid) {
78-
g_svml_handle = nullptr; // fork 后父进程 handle 在子进程无效
81+
g_svml_handle = nullptr;
7982
g_svml_pid = pid;
83+
g_svml_gen++; // 所有 resolve_cached 缓存失效
8084
}
8185
if (!g_svml_handle) {
8286
std::string path = find_umath_path();
@@ -87,6 +91,18 @@ inline void* resolve_svml(const char* name) {
8791
return nullptr;
8892
}
8993
94+
/// 带代缓存的符号解析。static 缓存 + fork 后自动重解析。
95+
template<typename FnSig>
96+
inline FnSig resolve_cached(const char* sym) {
97+
static FnSig fn = nullptr;
98+
static int gen = 0;
99+
if (gen != g_svml_gen) {
100+
fn = reinterpret_cast<FnSig>(resolve_svml(sym));
101+
gen = g_svml_gen;
102+
}
103+
return fn;
104+
}
105+
90106
// ============================================================================
91107
// Runtime CPU detection
92108
// ============================================================================
@@ -109,15 +125,19 @@ inline bool cpu_has_avx512f() {
109125
#define NUMPY_SVML_F64(name, svml_sym, npy_sym) \
110126
__attribute__((target("avx512f"))) \
111127
inline double name##_svml_f64(double x) { \
112-
auto fn = (__m512d (*)(__m512d))resolve_svml(svml_sym); \
128+
static auto fn = (__m512d(*)(__m512d))nullptr; \
129+
static int _gen = -1; \
130+
if (_gen != g_svml_gen) { fn = (__m512d(*)(__m512d))resolve_svml(svml_sym); _gen = g_svml_gen; } \
113131
if (fn) return _mm512_cvtsd_f64(fn(_mm512_set1_pd(x))); \
114-
return std::name(x); /* fallback if SVML resolution fails */ \
132+
return std::name(x); \
115133
}
116134
117135
#define NUMPY_SVML_F32(name, svml_sym, npy_sym) \
118136
__attribute__((target("avx512f"))) \
119137
inline float name##_svml_f32(float x) { \
120-
auto fn = (__m512 (*)(__m512))resolve_svml(svml_sym); \
138+
static auto fn = (__m512(*)(__m512))nullptr; \
139+
static int _gen = -1; \
140+
if (_gen != g_svml_gen) { fn = (__m512(*)(__m512))resolve_svml(svml_sym); _gen = g_svml_gen; } \
121141
if (fn) return _mm512_cvtss_f32(fn(_mm512_set1_ps(x))); \
122142
return std::name(x); \
123143
}
@@ -153,34 +173,34 @@ NUMPY_SVML_F32(log1p, "__svml_log1pf16","npy_log1pf")
153173
// SVML 实现位级一致(npy_pow / npy_atan2 是标量 libm 回退,会差 1 ULP)。
154174
__attribute__((target("avx512f")))
155175
inline double pow_svml_f64(double x, double e) {
156-
auto fn = (__m512d (*)(__m512d, __m512d))resolve_svml("__svml_pow8");
176+
static auto fn = (__m512d(*)(__m512d,__m512d))nullptr;
177+
static int _gen = -1;
178+
if (_gen != g_svml_gen) { fn = (__m512d(*)(__m512d,__m512d))resolve_svml("__svml_pow8"); _gen = g_svml_gen; }
157179
if (fn) return _mm512_cvtsd_f64(fn(_mm512_set1_pd(x), _mm512_set1_pd(e)));
158-
auto scalar_fn = (double (*)(double, double))resolve_svml("npy_pow");
159-
if (scalar_fn) return scalar_fn(x, e);
160180
return std::pow(x, e);
161181
}
162182
__attribute__((target("avx512f")))
163183
inline float pow_svml_f32(float x, float e) {
164-
auto fn = (__m512 (*)(__m512, __m512))resolve_svml("__svml_powf16");
184+
static auto fn = (__m512(*)(__m512,__m512))nullptr;
185+
static int _gen = -1;
186+
if (_gen != g_svml_gen) { fn = (__m512(*)(__m512,__m512))resolve_svml("__svml_powf16"); _gen = g_svml_gen; }
165187
if (fn) return _mm512_cvtss_f32(fn(_mm512_set1_ps(x), _mm512_set1_ps(e)));
166-
auto scalar_fn = (float (*)(float, float))resolve_svml("npy_powf");
167-
if (scalar_fn) return scalar_fn(x, e);
168188
return std::pow(x, e);
169189
}
170190
__attribute__((target("avx512f")))
171191
inline double atan2_svml_f64(double y, double x) {
172-
auto fn = (__m512d (*)(__m512d, __m512d))resolve_svml("__svml_atan28");
192+
static auto fn = (__m512d(*)(__m512d,__m512d))nullptr;
193+
static int _gen = -1;
194+
if (_gen != g_svml_gen) { fn = (__m512d(*)(__m512d,__m512d))resolve_svml("__svml_atan28"); _gen = g_svml_gen; }
173195
if (fn) return _mm512_cvtsd_f64(fn(_mm512_set1_pd(y), _mm512_set1_pd(x)));
174-
auto scalar_fn = (double (*)(double, double))resolve_svml("npy_atan2");
175-
if (scalar_fn) return scalar_fn(y, x);
176196
return std::atan2(y, x);
177197
}
178198
__attribute__((target("avx512f")))
179199
inline float atan2_svml_f32(float y, float x) {
180-
auto fn = (__m512 (*)(__m512, __m512))resolve_svml("__svml_atan2f16");
200+
static auto fn = (__m512(*)(__m512,__m512))nullptr;
201+
static int _gen = -1;
202+
if (_gen != g_svml_gen) { fn = (__m512(*)(__m512,__m512))resolve_svml("__svml_atan2f16"); _gen = g_svml_gen; }
181203
if (fn) return _mm512_cvtss_f32(fn(_mm512_set1_ps(y), _mm512_set1_ps(x)));
182-
auto scalar_fn = (float (*)(float, float))resolve_svml("npy_atan2f");
183-
if (scalar_fn) return scalar_fn(y, x);
184204
return std::atan2(y, x);
185205
}
186206
@@ -196,14 +216,24 @@ inline float atan2_svml_f32(float y, float x) {
196216
197217
#define NUMPY_NPY_F64(name, fallback_expr) \
198218
inline double name##_npy_f64(double x) { \
199-
auto fn = (double (*)(double))resolve_svml("npy_" #name); \
219+
static auto fn = (double(*)(double))nullptr; \
220+
static int _gen = -1; \
221+
if (_gen != g_svml_gen) { \
222+
fn = (double(*)(double))resolve_svml("npy_" #name); \
223+
_gen = g_svml_gen; \
224+
} \
200225
if (fn) return fn(x); \
201226
return (fallback_expr); \
202227
}
203228
204229
#define NUMPY_NPY_F32(name, fallback_expr) \
205230
inline float name##_npy_f32(float x) { \
206-
auto fn = (float (*)(float))resolve_svml("npy_" #name "f"); \
231+
static auto fn = (float(*)(float))nullptr; \
232+
static int _gen = -1; \
233+
if (_gen != g_svml_gen) { \
234+
fn = (float(*)(float))resolve_svml("npy_" #name "f"); \
235+
_gen = g_svml_gen; \
236+
} \
207237
if (fn) return fn(x); \
208238
return (fallback_expr); \
209239
}
@@ -247,22 +277,30 @@ inline double hypot_f64(double x, double y) { return std::hypot(x, y); }
247277
inline float hypot_f32(float x, float y) { return std::hypot(x, y); }
248278
249279
inline double pow_npy_f64(double x, double e) {
250-
auto fn = (double (*)(double, double))resolve_svml("npy_pow");
280+
static auto fn = (double(*)(double,double))nullptr;
281+
static int _gen = -1;
282+
if (_gen != g_svml_gen) { fn = (double(*)(double,double))resolve_svml("npy_pow"); _gen = g_svml_gen; }
251283
if (fn) return fn(x, e);
252284
return std::pow(x, e);
253285
}
254286
inline float pow_npy_f32(float x, float e) {
255-
auto fn = (float (*)(float, float))resolve_svml("npy_powf");
287+
static auto fn = (float(*)(float,float))nullptr;
288+
static int _gen = -1;
289+
if (_gen != g_svml_gen) { fn = (float(*)(float,float))resolve_svml("npy_powf"); _gen = g_svml_gen; }
256290
if (fn) return fn(x, e);
257291
return std::pow(x, e);
258292
}
259293
inline double atan2_npy_f64(double y, double x) {
260-
auto fn = (double (*)(double, double))resolve_svml("npy_atan2");
294+
static auto fn = (double(*)(double,double))nullptr;
295+
static int _gen = -1;
296+
if (_gen != g_svml_gen) { fn = (double(*)(double,double))resolve_svml("npy_atan2"); _gen = g_svml_gen; }
261297
if (fn) return fn(y, x);
262298
return std::atan2(y, x);
263299
}
264300
inline float atan2_npy_f32(float y, float x) {
265-
auto fn = (float (*)(float, float))resolve_svml("npy_atan2f");
301+
static auto fn = (float(*)(float,float))nullptr;
302+
static int _gen = -1;
303+
if (_gen != g_svml_gen) { fn = (float(*)(float,float))resolve_svml("npy_atan2f"); _gen = g_svml_gen; }
266304
if (fn) return fn(y, x);
267305
return std::atan2(y, x);
268306
}
@@ -279,21 +317,21 @@ inline float atan2_npy_f32(float y, float x) {
279317
// Without -mavx512f: always use the scalar npy_* path — still bit-exact.
280318
// ============================================================================
281319
320+
// 1-arg: SVML 优先 — __svml_exp8 等在非 AVX-512 上也是位级一致 (1000/1000)。
321+
// 2-arg: npy_* 优先 — pow_f64/atan2_f64 下方单独定义。
282322
#ifdef __AVX512F__
283-
#define DISPATCH_F64(name) \
284-
inline double name##_f64(double x) { \
285-
if (cpu_has_avx512f()) return name##_svml_f64(x); \
286-
return name##_npy_f64(x); \
287-
}
288-
#define DISPATCH_F32(name) \
289-
inline float name##_f32(float x) { \
290-
if (cpu_has_avx512f()) return name##_svml_f32(x); \
291-
return name##_npy_f32(x); \
292-
}
323+
#define DISPATCH_F64(name) \
324+
inline double name##_f64(double x) { \
325+
if (cpu_has_avx512f()) return name##_svml_f64(x); \
326+
return name##_npy_f64(x); }
327+
#define DISPATCH_F32(name) \
328+
inline float name##_f32(float x) { \
329+
if (cpu_has_avx512f()) return name##_svml_f32(x); \
330+
return name##_npy_f32(x); }
293331
#else
294-
#define DISPATCH_F64(name) \
332+
#define DISPATCH_F64(name) \
295333
inline double name##_f64(double x) { return name##_npy_f64(x); }
296-
#define DISPATCH_F32(name) \
334+
#define DISPATCH_F32(name) \
297335
inline float name##_f32(float x) { return name##_npy_f32(x); }
298336
#endif
299337
@@ -307,7 +345,7 @@ inline double sin_f64(double x) {
307345
#else
308346
double r = sin_npy_f64(x);
309347
#endif
310-
if (__builtin_expect(x == 0.0 && r == 0.0, 0)) return x; // ±0 → ±0
348+
if (__builtin_expect(x == 0.0 && r == 0.0, 0)) return x;
311349
return r;
312350
}
313351
DISPATCH_F64(cos)

0 commit comments

Comments
 (0)