внёс изменения, файлы сейчас: #include "pch.h" BO...
Erstellt am: 19. September 2025
Beantwortet mit GPT-5 Thinking von Chat01
Erstellt am: 19. September 2025
Beantwortet mit GPT-5 Thinking von Chat01
внёс изменения, файлы сейчас: #include "pch.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
}
return TRUE;
} - dllmain, static std::unique_ptrboost::mpi::environment g_env;
static std::unique_ptrboost::mpi::communicator g_world;
static std::unique_ptr<PeanoCurve_2D> Curve, Curve_Inverted;
extern "C" __declspec(dllexport) int __cdecl AgpInit(int peanoLevel, float a, float b, float c, float d) {
if (!g_env) g_env = std::make_uniqueboost::mpi::environment(); // MPI_Init()
if (!g_world) g_world = std::make_uniqueboost::mpi::communicator(); // MPI_COMM_WORLD
int rank = g_world->rank();
if (rank == 0) Curve = std::make_unique<PeanoCurve_2D>(List::Top, peanoLevel, a, b, c, d);
else if (rank == 1) Curve_Inverted = std::make_unique<PeanoCurve_2D>(List::Down, peanoLevel, a, b, c, d);
return rank;
}
extern "C" __declspec(dllexport) void __cdecl AgpFinalize() {
Curve_Inverted.reset();
Curve.reset();
g_world.reset();
g_env.reset(); // MPI_Finalize()
}
__declspec(align(16u)) struct CrossMsg final {
float s_x1, s_x2;
float e_x1, e_x2;
float Rtop;
template <typename Archive>
__declspec(noalias) __forceinline void serialize(Archive& __restrict ar, const uint64_t) noexcept { ar& s_x1& s_x2& e_x1& e_x2& Rtop; }
};
extern "C" __declspec(dllexport) __declspec(noalias) void Base_LNA_1_2_Mer_AGP(
const float N, const float global_iterations, const float a, const float b, const float c, const float d, const float r,
const bool mode, const float epsilon, const double seed,
float** out_data, uint64_t* out_len) noexcept
{
if (N == 1.0f) {
const float initial_length = b - a;
float dmax = initial_length;
const float start_val = ShekelFunc(a, seed);
float best_f = ShekelFunc(b, seed);
float schetchick = 0.0f;
textstd::pair<float, float> x_Rmax{ a, b }; std::pair<float, float> y_Rmax{ start_val, best_f }; std::vector<float> Extr; Extr.reserve(global_iterations * 4u); std::vector<std::unique_ptr<Interval>> R; R.reserve(global_iterations * 2u); // Создаем начальный интервал R.emplace_back(std::make_unique<Interval>(std::make_pair(a, start_val), std::make_pair(b, best_f), N)); float Mmax = R.front()->M; float m = r * Mmax; const float threshold_03 = 0.3f * initial_length; const float inv_threshold_03 = 1.0f / threshold_03; while (true) { const float new_point = Shag(m, x_Rmax.first, x_Rmax.second, y_Rmax.first, y_Rmax.second, N, r); const float new_value = ShekelFunc(new_point, seed); const std::pair<float, float> promejutochnaya_tochka{ new_point, new_value }; if (new_value < best_f) { best_f = new_value; Extr.emplace_back(best_f); Extr.emplace_back(new_point); } // Извлекаем максимальный интервал std::pop_heap(R.begin(), R.end(), ComparePtr); const std::unique_ptr<Interval> promejutochny_otrezok = std::move(R.back()); R.pop_back(); const size_t r_size = R.size(); const float len2 = promejutochny_otrezok->end.first - new_point; const float len1 = new_point - promejutochny_otrezok->start.first; const float dmin = len1 < len2 ? len1 : len2; // Условие завершения if (++schetchick == global_iterations || dmin < epsilon) { Extr.emplace_back(schetchick); Extr.emplace_back(dmin); // Копируем данные в выходной буфер *out_len = Extr.size(); *out_data = reinterpret_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len)); return; } // Создаем новые интервалы std::unique_ptr<Interval> curr = std::make_unique<Interval>(promejutochny_otrezok->start, promejutochnaya_tochka, N); std::unique_ptr<Interval> curr1 = std::make_unique<Interval>(promejutochnaya_tochka, promejutochny_otrezok->end, N); const float currM = curr->M > curr1->M ? curr->M : curr1->M; if (len2 + len1 == dmax) { dmax = len2 > len1 ? len2 : len1;
#pragma loop(ivdep)
for (size_t i = 0u; i < r_size; ++i) {
const float len = R[i]->end.first - R[i]->start.first;
if (len > dmax) {
dmax = len;
}
}
}
textif (mode) { if (threshold_03 > dmax && fmodf(schetchick, 3.0f) == 0.0f || 10.0f * dmax < initial_length) { if (currM > Mmax) { Mmax = currM; m = r * Mmax; } const float progress = fmaf(-inv_threshold_03, dmax, 1.0f); const float alpha = fmaf(progress, progress, 1.0f); const float betta = 2.0f - alpha; const float MULTIPLIER = (1.0f / dmax) * Mmax; const float global_coeff = fmaf(MULTIPLIER, r, -MULTIPLIER); const float GLOBAL_FACTOR = betta * global_coeff; curr->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len1, curr->M * alpha)); curr1->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len2, curr1->M * alpha));
#pragma loop(ivdep)
for (size_t i = 0u; i < r_size; ++i) {
const float len_item = R[i]->end.first - R[i]->start.first;
R[i]->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len_item, R[i]->M * alpha));
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
if (currM > Mmax) {
Mmax = currM;
m = r * Mmax;
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
#pragma loop(ivdep)
for (size_t i = 0u; i < r_size; ++i) {
R[i]->ChangeCharacteristic(m);
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
}
}
}
else {
if (currM > Mmax) {
Mmax = currM;
m = r * Mmax;
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
#pragma loop(ivdep)
for (size_t i = 0u; i < r_size; ++i) {
R[i]->ChangeCharacteristic(m);
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
}
}
text// Добавляем новые интервалы R.emplace_back(std::move(curr)); R.emplace_back(std::move(curr1)); std::push_heap(R.begin(), R.end(), ComparePtr); // Обновляем x_Rmax и y_Rmax const std::unique_ptr<Interval>& top = R.front(); x_Rmax = { top->start.first, top->end.first }; y_Rmax = { top->start.second, top->end.second }; } } else { // Простая заглушка для двумерной ветки const int rank = g_world->rank(); const int partner = (rank == 0) ? 1 : 0; // Простые вычисления float simple_value = rank * 10.0f + 5.0f; float received_value = 0.0f; // Простая MPI-коммуникация if (rank == 0) { g_world->send(partner, 0, simple_value); g_world->recv(partner, 0, received_value); } else { g_world->recv(partner, 0, received_value); received_value += 1.0f; // Небольшое вычисление g_world->send(partner, 0, received_value); } // Создаем небольшой вектор с результатами std::vector<float> simple_results; simple_results.push_back(simple_value); simple_results.push_back(received_value); simple_results.push_back(static_cast<float>(rank)); // Копируем данные в выходной буфер *out_len = simple_results.size(); *out_data = reinterpret_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, simple_results.data(), sizeof(float) * (*out_len)); // Синхронизация перед завершением g_world->barrier(); return; }
}
extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float* p) {
CoTaskMemFree(p);
}
// В DLL
extern "C" __declspec(dllexport) void __cdecl AgpStartWorkers() {
int go = 1;
MPI_Bcast(&go, 1, MPI_INT, /root=/0, MPI_COMM_WORLD);
}
extern "C" __declspec(dllexport) void __cdecl AgpWaitStartAndRun() {
int go = 0;
MPI_Bcast(&go, 1, MPI_INT, /root=/0, MPI_COMM_WORLD); // блокируется, пока мастер не пошлёт
if (go == 1) {
float* dummy = nullptr; uint64_t dummy_len = 0;
Base_LNA_1_2_Mer_AGP(2.0f, 10000.0f, -2.2f, 1.8f, -2.2f, 1.8f,
2.5f, false, 0.0001f, GetTickCount(),
&dummy, &dummy_len);
if (dummy) Base_LNA_1_2_Mer_AGP_Free(dummy);
}
} - основной .cpp файл библиотеки с заглушкой, #include "MyForm.h"
#include <float.h>
using namespace System;
using namespace System::Windows::Forms;
typedef int (*PInit)(int, float, float, float, float);
typedef void (*PFin)();
[STAThread]
int main() {
HMODULE h = LoadLibraryW(L"TEST_FUNC.dll");
auto AgpInit = (PInit)GetProcAddress(h, "AgpInit");
auto AgpFinalize = (PFin)GetProcAddress(h, "AgpFinalize");
if (!h || !AgpInit || !AgpFinalize) { MessageBox::Show(L"Не удалось инициализировать DLL/MPI"); return 1; }
textconst int rank = AgpInit(12, -2.2f, 1.8f, -2.2f, 1.8f); if (rank == 0) { Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); TESTAGP::MyForm form(h); Application::Run(% form); //Application::Run(gcnew TESTAGP::MyForm(h)); } else { // Воркер-процесс: ждёт команды "старт" и выполняет расчёт без UI typedef void(__cdecl* PStartWorkers)(); auto AgpWaitStartAndRun = (PStartWorkers)GetProcAddress(h, "AgpWaitStartAndRun"); if (AgpWaitStartAndRun) AgpWaitStartAndRun(); // см. пункт D } AgpFinalize(); FreeLibrary(h); return 0;
} - MyForm.cpp, ref class MyForm : public System::Windows::Forms::Form {
public:
MyForm(HMODULE hLib) { // Передаем дескриптор из main
this->hLib = hLib;
InitializeComponent();
// Загружаем функции из DLL
f = (agp_c)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP");
ffree = (agp_free)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP_Free");
pStart = (start_workers)GetProcAddress(hLib, "AgpStartWorkers");
}
textprotected: ~MyForm() { delete components; } private: HINSTANCE hLib = nullptr; typedef void(*agp_c)(float, float, float, float, float, float, float, bool, float, double, float**, uint64_t*); typedef void(*agp_free)(float*); typedef void(__cdecl* start_workers)(); agp_c f = nullptr; agp_free ffree = nullptr; start_workers pStart = nullptr; System::Windows::Forms::Button^ button1; System::Windows::Forms::TextBox^ textBox2; System::Windows::Forms::DataVisualization::Charting::Chart^ chart2; System::Windows::Forms::Label^ label2; System::Windows::Forms::Label^ label3; System::Windows::Forms::TextBox^ textBox1; System::Windows::Forms::TextBox^ textBox3; System::Windows::Forms::TextBox^ textBox4; System::Windows::Forms::TextBox^ textBox5; System::Windows::Forms::TextBox^ textBox6; System::Windows::Forms::Label^ label6; System::Windows::Forms::Label^ label7; System::Windows::Forms::Label^ label8; System::Windows::Forms::Label^ label9; System::Windows::Forms::Label^ label10; System::Windows::Forms::Label^ label1; System::Windows::Forms::TextBox^ textBox7; System::Windows::Forms::TextBox^ textBox8; System::ComponentModel::Container^ components; System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { chart2->Series[0]->Points->Clear(); chart2->Series[1]->Points->Clear(); chart2->Series[2]->Points->Clear(); chart2->Series[3]->Points->Clear(); static LARGE_INTEGER start, end; QueryPerformanceCounter(&start); const double seed = GetTickCount(); float* buf = nullptr; uint64_t len = 0u; // Вызов для 2D ветки (N = 2.0f) //auto pStart = (void(__cdecl*)())GetProcAddress(hLib, "AgpStartWorkers"); if (pStart) pStart(); // разбудили rank != 0 f(2.0f, 10000.0f, -2.2f, 1.8f, -2.2f, 1.8f, 2.5f, false, 0.0001f, seed, &buf, &len); if (buf != nullptr) { std::vector<float> Extr_2D(buf, buf + len); ffree(buf); - MyForm.h, при запуске через mpiexec получаю: C:\Users\maxim>mpiexec -n 2 D:\TEST_AGP\x64\Release\TEST_AGP.exe
Необработанное исключение: System.Runtime.InteropServices.SEHException: Внешний компонент создал исключение.
в main() в D:\TEST_AGP\MyForm.cpp:строка 28
в mainCRTStartup()
job aborted:
[ranks] message
[0] terminated
[1] process exited without calling finalize
---- error analysis -----
[1] on DESKTOP-C1VT6JF
D:\TEST_AGP\x64\Release\TEST_AGP.exe ended prematurely and may have crashed. exit code 0xc000001d
---- error analysis ----- - внимательно проверь все сигнатуры, типы, код ошибки, правильность инициализации mpi, о чём говорит ошибка и в чём скорее всего дело
Коротко: ваш креш с кодом 0xC000001D — это EXCEPTION_ILLEGAL_INSTRUCTION (“недопустимая инструкция”). В практике Windows такое чаще всего случается из-за несовпадения соглашений о вызовах/прототипов при GetProcAddress
(регистры/стек портятся → прыжок в мусор/после ретёрна CPU читает «инструкции» из мусора) или потому что бинарник собран с инструкциями (AVX/AVX2/FMA), которых нет у процессора. По вашему коду основной красный флаг — несогласованное соглашение о вызове у Base_LNA_1_2_Mer_AGP
: экспорт объявлен без __cdecl
, а у вас может быть включён /Gv
(__vectorcall по умолчанию). Тогда снаружи вы вызываете её как __cdecl
→ UB и часто именно 0xC000001D.
Ниже — разбор по пунктам, что проверить и что поправить.
AgpInit
/ AgpFinalize
— ок, явно extern "C"
+ __cdecl
, и такие же typedef’ы.AgpStartWorkers
/ AgpWaitStartAndRun
— ок, тоже __cdecl
и совпадающие typedef’ы.Base_LNA_1_2_Mer_AGP
— экспорт без явного __cdecl
.MyForm.h
typedef agp_c
тоже без явного __cdecl
.На x64 по умолчанию действительно «как бы» __cdecl
, но если в проекте DLL стоит Project → C/C++ → Advanced → Calling Convention = /Gv
(то есть __vectorcall
по умолчанию) — эта функция внутри DLL станет __vectorcall
, а вы извне зовёте её как __cdecl
. Это типичный рецептик для «недопустимой инструкции».
__cdecl
ко всем экспортам, в т.ч. к двум «большим» функциям:cppextern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP( float N, float global_iterations, float a, float b, float c, float d, float r, bool mode, float epsilon, double seed, float** out_data, uint64_t* out_len) noexcept; extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float* p);
__cdecl
в typedef’ах:cpptypedef int (__cdecl *PInit)(int, float, float, float, float); typedef void (__cdecl *PFin)(); typedef void (__cdecl *agp_c)(float, float, float, float, float, float, float, bool, float, double, float**, uint64_t*); typedef void (__cdecl *agp_free)(float*); typedef void (__cdecl *start_workers)();
GetProcAddress
обязательно проверять все указатели (не только pStart
, как сейчас), и если nullptr
— показать GetLastError()
и не вызывать:cppf = reinterpret_cast<agp_c>(GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP")); ffree = reinterpret_cast<agp_free>(GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP_Free")); if (!f || !ffree) { MessageBox::Show(L"Не найден экспорт Base_LNA... в DLL"); AgpFinalize(); FreeLibrary(hLib); return 1; }
/Gv
). Если очень нужен __vectorcall
, помечайте локально конкретные функции в обоих местах одинаково.Иногда имя в экспорте не совпадает с тем, что вы ищете (особенно при разных соглашениях вызова на x86). Быстрый тест:
dumpbin /exports TEST_FUNC.dll
Убедитесь, что видите ровно AgpInit
, AgpFinalize
, AgpStartWorkers
, AgpWaitStartAndRun
,
Base_LNA_1_2_Mer_AGP
, Base_LNA_1_2_Mer_AGP_Free
(без декораций). Если название иное — GetProcAddress
вернёт NULL
, а вызов «мусора» даст тот же 0xC000001D.
0xC000001D = Illegal instruction.
На втором месте после несоответствия calling convention — инструкции AVX/AVX2/FMA на процессоре без поддержки. Если у вас в проекте выставлено /arch:AVX2 (или включено автогенерирование FMA с /fp:fast
) — попробуйте временно пересобрать DLL и EXE с /arch:SSE2 (или без AVX/FMA) и проверить. Особенно характерно, если падает в Release сразу при входе в функцию.
Неверный прототип может повредить регистры/стек и привести к прыжку «в никуда». Это и даёт эффект «недопустимой инструкции». Ваша ситуация этому полностью соответствует из-за отсутствия __cdecl
у Base_LNA_1_2_Mer_AGP
.
Ваши зоны риска здесь:
boost::mpi::environment
в AgpInit
и уничтожаете в AgpFinalize
— хорошо. В DllMain
ничего лишнего (только DisableThreadLibraryCalls
) — тоже правильно.MPI_Init/Finalize
вызываются ровно один раз на процесс (у вас это делает boost::mpi::environment
). Это условие выполняется.MPI_Bcast
для старта воркеров: ваш AgpStartWorkers()
(root=0 шлёт go=1
) и AgpWaitStartAndRun()
(ждёт, затем запускает) — корректная схема. Ловушка: не вызывайте AgpStartWorkers()
повторно, иначе воркер будет ждать второй широковещательный вызов.g_world->barrier();
в конце ветки N != 1.0f
, это правильно, иначе один процесс мог бы уйти в MPI_Finalize
, пока другой в Bcast
.Весь этот блок выглядит адекватно; он не объясняет 0xC000001D. Основной источник креша — выше (ABI/инструкции).
LoadLibrary
и все функции (вы уже проверяете часть, но добавьте f
/ffree
, см. выше).CoTaskMemAlloc/CoTaskMemFree
— безопасно между разными CRT, так и оставьте.extern "C"
у всех экспортов — уже хорошо. Сохраните везде одинаковый набор: extern "C" __declspec(dllexport) __cdecl
.bool
на MSVC — 1 байт и в нативном C++/CLI это тот же bool
, здесь ок. uint64_t*
совпадает на x64.__declspec(noalias)
, __forceinline
, без /GL
(LTCG) — чтобы упростить стек и символы на время отладки.DLL (заголовки экспортов):
cppextern "C" __declspec(dllexport) int __cdecl AgpInit(int, float, float, float, float); extern "C" __declspec(dllexport) void __cdecl AgpFinalize(); extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP( float N, float global_iterations, float a, float b, float c, float d, float r, bool mode, float epsilon, double seed, float** out_data, uint64_t* out_len) noexcept; extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float* p); extern "C" __declspec(dllexport) void __cdecl AgpStartWorkers(); extern "C" __declspec(dllexport) void __cdecl AgpWaitStartAndRun();
EXE (typedef’ы):
cpptypedef int (__cdecl *PInit)(int, float, float, float, float); typedef void (__cdecl *PFin)(); typedef void (__cdecl *agp_c)(float, float, float, float, float, float, float, bool, float, double, float**, uint64_t*); typedef void (__cdecl *agp_free)(float*); typedef void (__cdecl *start_workers)();
И проверки на nullptr
для f
/ffree
.
Base_LNA_1_2_Mer_AGP
(в DLL могла стать __vectorcall
, а вызываете как __cdecl
). Добавьте явный __cdecl
и в экспорт, и в typedef — это критично.После этих правок запуск через
mpiexec -n 2 D:\TEST_AGP\x64\Release\TEST_AGP.exe
должен перестать падать уже на входе. Если после исправления ABI появятся уже «обычные» ошибки (например, AV/таймаут/дедлок), их будет проще отладить дальше.