в своей dll в основной экспортируемой функции у ме...
作成日: 2025年9月23日
使用モデル GPT-5 Thinking by Chat01
作成日: 2025年9月23日
使用モデル GPT-5 Thinking by Chat01
в своей dll в основной экспортируемой функции у меня есть вызов статической функции из основного файла dll - эта статическая функция использует статическую реализацию синуса, когда я использую стандартный синус - то ошибок нет - но когда свой синус - то возникают ошибки - причём дело точно не в реализациях - я проверил их изолированно - все из них корректны - может дело в уровне вложенности? получается что экспортируемая функция вызывает функцию которая вызывает функцию - может передавать аргументы по ссылкам или указателям или умным указателям? вызывающее dll приложение - это управляемый winforms код и там надо быть очень аккуратным со стеком а тут как раз вложенность влияет на стек, любые неаккуратные действия со стеком сразу приводят к ошибкам в управляемом коде - проверено - можно ли как-то сделать так чтобы был не стек вызовов а может куча вызывов - просто что-то другое или может тогда одну из вложенных функций тоже сделать экспортируемой? если я сделаю ещё одну dll которую будет вызывать эта dll то это не поможет? чтобы уменьшить уровень вложенности, ведь эта dll использует и другие dll такие как например tbb? static __declspec(noalias) __forceinline float LCG_rand(double& __restrict const state) noexcept
{
if (!state) {
state = GetTickCount();
}
state = fma(state, 1.618033988749895, state * 0.5772156649);
state -= floor(state);
state = fma(state, state + 1.0, 0.0);
state -= floor(state);
return state;
}
static __declspec(noalias) __forceinline float ShekelFunc(const float x, const double seed) noexcept
{
uint64_t i = 0u;
double current_state = seed;
float res = 0.0f;
textwhile (i < 10u) { const float x_part = fmaf(-LCG_rand(current_state), 10.0f, x); float delimiter = fmaf(fmaf(LCG_rand(current_state), 20.0f, 5.0f), x_part * x_part, fmaf(LCG_rand(current_state), 0.2f, 1.0f)); delimiter = copysignf(fmaxf(fabsf(delimiter), FLT_MIN), delimiter); res -= 1.0f / delimiter; ++i; } return res;
} - но при этом заметь такая комбинация работает, хотя тут тот же уровень вложенности, в чём особенность такой комбинации? может быть здесь функция LCG вобоще не использует стек? в чём её особенность которая позволяет так делать? но уже такая комбинация: static __declspec(noalias) __forceinline float fabe13_cos1(const float x) noexcept {
const float ax = fabsf(x);
if (ax < 7.450580596923828125e-9f) { return 1.0f; }
const float k_dd = x * 0.6366197723675814f;
const int k_int = k_dd + (k_dd > 0.0f ? 0.5f : -0.5f);
const float k = k_int;
const float r = fmaf(-1.5707963267948966f, k, x);
const float r2 = r * r;
float sin_r = fmaf(fmaf(0.0083333318f, r2, -0.16666666f), r2, 1.0f) * r;
float cos_r = fmaf(fmaf(fmaf(-0.0013888398f, r2, 0.041666638f), r2, -0.5f), r2, 1.0f);
if (k == 0.0f) {
return cos_r;
}
else if (k == 1.0f) {
return -sin_r;
}
else if (k == 2.0f) {
return -cos_r;
}
else {
return sin_r;
}
} static __declspec(noalias) __forceinline float RastriginFunc(const float x1, const float x2) noexcept
{
const float term1 = fmaf(x1, x1, x2 * x2);
return (term1 - fmaf(
fabe13_cos(6.28318530717958647692f * x1) +
fabe13_cos(6.28318530717958647692f * x2),
10.0f,
-14.6f)) *
fmaf(-term1, 0.25f, 18.42f);
} - не работает
Коротко: проблема почти наверняка не в «глубине стека» и не в том, что функция вызывает функцию. В P/Invoke падения из-за стека почти всегда приходят от несовпадения соглашения о вызовах/сигнатур на границе .NET ↔ native, а не от внутренних вызовов в самой DLL.
Ниже — целевой чек-лист и конкретные правки.
__cdecl
.[DllImport]
по умолчанию CallingConvention.Winapi
(на x86 это чаще __stdcall
).Сделайте явно и согласованно:
C++ (экспорт):
cppextern "C" __declspec(dllexport) float __cdecl Rastrigin(float x1, float x2) noexcept { return RastriginFunc(x1, x2); }
C# (импорт):
csharp[DllImport("MyDll", ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)] internal static extern float Rastrigin(float x1, float x2);
На x64 одна конвенция, но я всё равно советую прописывать явно — код станет переносимее и очевиднее.
Типы параметров/возврата — строго совпадают.
float
↔ float
, double
↔ double
. Никаких «а тут float, а там double». Это критично именно на x86.
Имя функции, прототип и линковка.
В вашем примере вы определили fabe13_cos1
, а вызываете fabe13_cos(...)
. Если в реальном коде тоже так — легко получить вызов другой функции/прототипа (или вообще не того ABI) → UB/краш. Убедитесь, что:
static inline
и видны компилятору в том же TU.Не экспортируйте C++-референсы.
В вашем экспорте — только «plain C ABI»: простые скаляры, указатели на буферы, структуры с явной раскладкой. Внутри DLL можете использовать float&
/double&
сколько угодно, но наружу — ни в коем случае.
Не меняйте FPU-контрольные слова, или восстанавливайте.
Если где-то включили маски исключений (_controlfp
, _mm_setcsr
, /fp:except
) и схлопотали INVALID/OVERFLOW
, то управляемая сторона может падать. Без надобности не трогайте; либо сохраните/восстановите:
cpp#include <float.h> #include <immintrin.h> struct FpuGuard { unsigned int cw{}; unsigned int mxcsr{}; FpuGuard() { _controlfp_s(&cw, 0, 0); mxcsr = _mm_getcsr(); } ~FpuGuard() { _controlfp_s(&cw, cw, _MCW_IC | _MCW_RC | _MCW_PC | _MCW_EM); _mm_setcsr(mxcsr); } };
И на входе экспортируемой функции создайте FpuGuard guard;
.
__forceinline
, __declspec(noalias)
, __restrict
. Постройте без LTO/IPO. Так легче увидеть источник.LCG_rand
+ ShekelFunc
«работают» не потому, что там «меньше стека». Просто компилятор почти всё держит в регистрах/инлайнит, и на границе P/Invoke ничего не отличается.
RastriginFunc
c вашим fabe13_cos1
«не работает» — очень вероятно из-за ошибки имени/прототипа (fabe13_cos
vs fabe13_cos1
) или из-за рассинхронизации float/double
в объявлениях. Это даёт именно те симптомы, о которых вы пишете, и проявляется «вдруг» при замене cosf
на свою реализацию.
«Сделать ещё одну DLL» или «сделать внутреннюю функцию экспортируемой» не решит проблему стека. Граница .NET↔native одна; внутренняя вложенность на стек P/Invoke не влияет.
«Передавать по ссылкам/умным указателям» — внутри DLL делайте как хотите; на экспорт — только C-совместимые типы. Умные указатели/ссылки — нельзя.
Экспорт:
cppextern "C" __declspec(dllexport) float __cdecl Rastrigin(float x1, float x2) noexcept { // (опционально) защитим состояние FPU // FpuGuard guard; return RastriginFunc(x1, x2); } // внутренняя реализация — только internal linkage: static inline float fabe13_cos1(float x) noexcept { const float ax = fabsf(x); if (ax < 7.450580596923828125e-9f) return 1.0f; const float k_dd = x * 0.6366197723675814f; const int k_int = int(k_dd + (k_dd > 0.0 ? 0.5 : -0.5)); const float k = (float)k_int; const float r = fmaf(-1.5707963267948966f, k, x); const float r2 = r * r; const float sin_r = fmaf(fmaf(0.0083333318f, r2, -0.16666666f), r2, 1.0f) * r; const float cos_r = fmaf(fmaf(fmaf(-0.0013888398f, r2, 0.041666638f), r2, -0.5f), r2, 1.0f); return (k == 0.0f) ? cos_r : (k == 1.0f) ? -sin_r : (k == 2.0f) ? -cos_r : sin_r; } static inline float RastriginFunc(float x1, float x2) noexcept { const float term1 = fmaf(x1, x1, x2 * x2); const float c = 6.28318530717958647692f; // ВНИМАНИЕ: имя функции должно совпадать! const float osc = fabe13_cos1(c * x1) + fabe13_cos1(c * x2); return (term1 - fmaf(osc, 10.0f, -14.6f)) * fmaf(-term1, 0.25f, 18.42f); }
Импорт в C#:
csharp[DllImport("MyDll", ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)] internal static extern float Rastrigin(float x1, float x2);
/GS /sdl /Wall
, временно уберите __forceinline
.dumpbin /exports MyDll.dll
— убедитесь, что экспорт без @N
(это был бы stdcall), имя строго Rastrigin
.__declspec(noinline)
к RastriginFunc
/fabe13_cos1
— если краш не меняется, дело точно не в глубине стека/инлайне.Если после выравнивания конвенций и сигнатур проблема сохранится — пришлите, пожалуйста, точную сигнатуру экспортируемой функции (как в C++ экспорте, так и в [DllImport]
), битность процесса (x86/x64) и текст ошибки/MDAs. Но с высокой вероятностью всё решится на шагах выше.