@@ -40,18 +40,21 @@ Use #include "numpycpp/numpy.h" instead."
4040namespace numpy {
4141namespace 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
5053inline 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 缓存)
5558inline 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 作废 → 重建 。
7578inline 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" )))
155175inline 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" )))
163183inline 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" )))
171191inline 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" )))
179199inline 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); }
247277inline float hypot_f32(float x, float y) { return std::hypot(x, y); }
248278
249279inline 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}
254286inline 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}
259293inline 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}
264300inline 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}
313351DISPATCH_F64(cos)
0 commit comments