мой код в winforms.h : ref class MyForm : public ...
Создано: 18 сентября 2025 г.
Создано: 18 сентября 2025 г.
мой код в winforms.h : ref class MyForm : public System::Windows::Forms::Form {
public:
MyForm(void) {
InitializeComponent();
this->Load += gcnew System::EventHandler(this, &MyForm::MyForm_Load);
// Subscribe to FormClosed event
this->FormClosed += gcnew System::Windows::Forms::FormClosedEventHandler(this, &MyForm::MyForm_FormClosed);
}
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*); agp_c f = nullptr; agp_free ffree = 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 MyForm_Load(System::Object^ sender, System::EventArgs^ e) { // Загрузка библиотеки при запуске формы hLib = LoadLibrary(L"TEST_FUNC.dll"); f = (agp_c)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP"); ffree = (agp_free)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP_Free"); } 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) 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); // Для 2D ветки: извлекаем schetchick и interval_len textBox7->Text = Convert::ToString(Extr_2D.back()); // schetchick Extr_2D.pop_back(); textBox6->Text = Convert::ToString(Extr_2D.back()); // interval_len Extr_2D.pop_back(); // Извлекаем последнюю тройку (x2, x1, f) float x2 = Extr_2D.back(); Extr_2D.pop_back(); float x1 = Extr_2D.back(); Extr_2D.pop_back(); float f_val = Extr_2D.back(); Extr_2D.pop_back(); // Выводим координаты и значение функции textBox4->Text = Convert::ToString(x1); textBox3->Text = Convert::ToString(x2); textBox2->Text = Convert::ToString(f_val); // Новое поле для значения функции // Отображаем точку на графике chart2->Series[2]->Points->AddXY(x1, x2); // Обрабатываем остальные точки (в порядке: f, x1, x2) while (!Extr_2D.empty()) { float x2_point = Extr_2D.back(); Extr_2D.pop_back(); float x1_point = Extr_2D.back(); Extr_2D.pop_back(); float f_point = Extr_2D.back(); Extr_2D.pop_back(); // Добавляем точку на график (только координаты) chart2->Series[0]->Points->AddXY(x1_point, x2_point); } } QueryPerformanceCounter(&end); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); const uint64_t multiplier = ((1ULL << 32) / freq.QuadPart) * 1'000'000ULL; textBox5->Text = Convert::ToString( ((end.QuadPart - start.QuadPart) * multiplier) >> 32 ) + " microseconds"; //textBox2->Text = Convert::ToString(Extr_2D.back()); //textBox1->Text = Convert::ToString(Extr_2D_LNA.back()); } System::Void MyForm_FormClosed(System::Object^ sender, System::Windows::Forms::FormClosedEventArgs^ e) { if (hLib != nullptr) { FreeLibrary(hLib); hLib = nullptr; } } ... #include "MyForm.h"
#include <float.h>
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
int main() {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew TESTAGP::MyForm());
} - код в winforms.cpp, extern const boost::mpi::communicator world;
extern std::unique_ptr<PeanoCurve_2D> Curve;
extern std::unique_ptr<PeanoCurve_2D> Curve_Inverted;
__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 = world.rank(); const int partner = fmodf(rank, 2.0f) == 0 ? rank + 1 : rank - 1; const PeanoCurve_2D* __restrict LocCurve = rank ? Curve_Inverted.get() : Curve.get(); const float divider = powf(2.0f, fmaf(2.0f, LocCurve->razvertka, 1.0f)); const float inv_divider = 1.0f / divider; const float x_addition = (b - a) * inv_divider; const float y_addition = (d - c) * inv_divider; const float true_start = a + x_addition; const float true_end = b - x_addition; const float initial_length = true_end - true_start; float dmax = initial_length; const float end_val = rank ? RastriginFunc(true_end, d - y_addition) : RastriginFunc(true_start, c + y_addition); float best_f = rank ? RastriginFunc(true_start, d - y_addition) : RastriginFunc(true_end, c + y_addition); //const int rank = world.rank(); //const int partner = rank ^ 1; float schetchick = 0.0f; float mcQueenSpeed = 1.0f; std::pair<float, float> x_Rmax{ true_start, true_end }; std::pair<float, float> y_Rmax{ end_val, best_f }; std::vector<float> Extr; Extr.reserve(global_iterations * 3u + 4u); std::vector<std::unique_ptr<Interval>> R; R.reserve(global_iterations + 2u); // Прямые инициализации пар (избегаем лишних копий) R.emplace_back(std::make_unique<Interval>( std::make_pair(true_start, end_val), std::make_pair(true_end, 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) { if (const boost::optional<boost::mpi::status> status0 = world.iprobe(partner, 0)) { world.recv(partner, 0, mcQueenSpeed); } else if (const boost::optional<boost::mpi::status> status2 = world.iprobe(partner, 2)) { float received_flag = 0.0f; world.recv(partner, 2, received_flag); if (received_flag || ++schetchick == global_iterations) { // Завершение работы const std::unique_ptr<Interval>& front = R.front(); Extr.emplace_back(schetchick); Extr.emplace_back(front->end.first - front->start.first); *out_len = Extr.size(); *out_data = reinterpret_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len)); return; } } else if (++schetchick == global_iterations) { const std::unique_ptr<Interval>& front = R.front(); Extr.emplace_back(schetchick); Extr.emplace_back(front->end.first - front->start.first); // Сигналим партнёру о завершении world.send(partner, 2, 1.0f); *out_len = Extr.size(); *out_data = reinterpret_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len)); return; } // «Охлаждение» и период T (как в вашей 2D-версии) const float cooling = ldexpf(1.0f, -schetchick * (1.0f / 138.63f)); const float T = floorf(fmaf(20.0f, cooling, 10.0f)); const float k = fmaf(0.2f, cooling, 0.7f); if (!(fmodf(schetchick, T)) == mcQueenSpeed) { // Берём top без копирования (константная ссылка на уникальный указатель) const std::unique_ptr<Interval>& topL = R.front(); // HitTest для концов лучшего интервала const PeanoCurve_2D* __restrict p1L = LocCurve->HitTest_2D(topL->start.first); const PeanoCurve_2D* __restrict p2L = LocCurve->HitTest_2D(topL->end.first); const CrossMsg outbound{ p1L->x1, p1L->x2, p2L->x1, p2L->x2, topL->R }; CrossMsg inbound{}; if (mcQueenSpeed) { world.send(partner, 0, 0.0f); } else { mcQueenSpeed = 1.0f; } world.sendrecv(partner, 1, outbound, partner, 1, inbound); // Проекция удалённых точек обратно в параметр x по локальной кривой const float newParam1_remote = LocCurve->FindX_2D(inbound.s_x1, inbound.s_x2); const float newParam2_remote = LocCurve->FindX_2D(inbound.e_x1, inbound.e_x2); // Прямая инициализация пар, без лишних временных const std::pair<float, float> newStart_remote{ newParam1_remote, RastriginFunc(inbound.s_x1, inbound.s_x2) }; const std::pair<float, float> newEnd_remote{ newParam2_remote, RastriginFunc(inbound.e_x1, inbound.e_x2) }; // Вставляем «инъекцию» с пониженной характеристикой std::unique_ptr<Interval> injected = std::make_unique<Interval>(newStart_remote, newEnd_remote, N); injected->R = inbound.Rtop * k; R.emplace_back(std::move(injected)); std::push_heap(R.begin(), R.end(), ComparePtr); } // ===== Локальный шаг LNA в параметре кривой ===== const float new_point = Shag(m, x_Rmax.first, x_Rmax.second, y_Rmax.first, y_Rmax.second, /*_N*/ 2u, r); // Координаты по кривой Пеано const PeanoCurve_2D* pc = LocCurve->HitTest_2D(new_point); const float new_x1 = pc->x1; const float new_x2 = pc->x2; const float new_value = RastriginFunc(new_x1, new_x2); const std::pair<float, float> promejutochnaya_tochka{ new_point, new_value }; // Обновление экстремумов (формат записи — как решено в 2D-ветке) if (new_value < best_f) { best_f = new_value; Extr.emplace_back(best_f); Extr.emplace_back(new_x1); Extr.emplace_back(new_x2); } // ===== Разбиение лучшего интервала ===== std::pop_heap(R.begin(), R.end(), ComparePtr); const std::unique_ptr<Interval> promejutochny_otrezok = std::move(R.back()); R.pop_back(); // Создаём подынтервалы с прямой инициализацией std::unique_ptr<Interval> curr = std::make_unique<Interval>(promejutochny_otrezok->start, promejutochnaya_tochka, /*_N*/ 2u); std::unique_ptr<Interval> curr1 = std::make_unique<Interval>(promejutochnaya_tochka, promejutochny_otrezok->end, /*_N*/ 2u); const float currM = (curr->M > curr1->M ? curr->M : curr1->M); const float len2 = promejutochny_otrezok->end.first - new_point; const float len1 = new_point - promejutochny_otrezok->start.first; const size_t r_size = R.size(); // Обновление dmax — один проход без лишних вызовов 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;
}
}
text// ===== Перенастройка характеристик R (как в 1D-ветке, но для N=2) ===== if (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) {
R[i]->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, R[i]->end.first - R[i]->start.first, R[i]->M * alpha));
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
if (currM > Mmax) {
Mmax = currM;
m = r * Mmax;
textcurr->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;
textcurr->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); // достаточно одного push_heap после двух вставок // Проверка критерия сходимости по «лучшему» интервалу const std::unique_ptr<Interval>& top = R.front(); const float interval_len = top->end.first - top->start.first; if (interval_len < epsilon) { Extr.emplace_back(schetchick); Extr.emplace_back(interval_len); world.send(partner, 2, 1.0f); *out_len = Extr.size(); *out_data = reinterpret_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len)); return; } // Обновляем x_Rmax и y_Rmax (константная ссылка на top, чтобы избежать копии) x_Rmax = { top->start.first, top->end.first }; y_Rmax = { top->start.second, top->end.second }; } }
}
extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float* p) {
CoTaskMemFree(p);
} код в основном .cpp приложении моей dll библиотеки для экспорта, код dllmain.cpp: #include "pch.h"
const boost::mpi::environment mpi_env;
extern const boost::mpi::communicator world;
const boost::mpi::communicator world;
std::unique_ptr<PeanoCurve_2D> Curve;
std::unique_ptr<PeanoCurve_2D> Curve_Inverted;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
const int32_t rank = world.rank();
if (rank == 0u) {
Curve = std::make_unique<PeanoCurve_2D>(List::Top, 12u, -2.2f, 1.8f, -2.2f, 1.8f);
}
else if (rank == 1u) {
Curve_Inverted = std::make_unique<PeanoCurve_2D>(List::Down, 12u, -2.2f, 1.8f, -2.2f, 1.8f);
}
}
return TRUE;
} - проблема в том что когда я запускаю основное приложение через mpiexec указывая что должно быть только два процесса то каждый процесс открывает свою форму и получается две формы, после того как я нажал кнопку на одной из них то получил: C:\Users\maxim>mpiexec -n 2 D:\TEST_AGP\x64\Release\TEST_AGP.exe
Необработанное исключение: System.Runtime.InteropServices.SEHException: Внешний компонент создал исключение.
в TESTAGP.MyForm.button1_Click(Object sender, EventArgs e) в D:\TEST_AGP\MyForm.h:строка 79
в System.Windows.Forms.Control.OnClick(EventArgs e)
в System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
в System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
в System.Windows.Forms.Control.WndProc(Message& m)
в System.Windows.Forms.ButtonBase.WndProc(Message& m)
в System.Windows.Forms.Button.WndProc(Message& m)
в System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
в System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
в System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
в System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
в System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
в main() в D:\TEST_AGP\MyForm.cpp:строка 14
в mainCRTStartup()
job aborted:
[ranks] message
[0] process exited without calling finalize
[1] terminated
---- error analysis -----
[0] on DESKTOP-C1VT6JF
D:\TEST_AGP\x64\Release\TEST_AGP.exe ended prematurely and may have crashed. exit code 0xc000001d
---- error analysis -----
C:\Users\maxim> мне не нужно чтобы mpi как либо взаимодействовал с GUI, мне нужно чтобы MPI использовался только в dll библиотеке для создания кривых и параллельного обхода по кривым, как мне лучше поступить чтобы получить максимальный выигрыш по скорости от mpi и чтобы mpi не влиял на GUI? И в папки с основным приложением и в папки с dll добавлены необходимые dll файлы для работы с mpi, мне хотелось бы сохранить инициализацию MPI контекста по загрузке dll
Коротко: вы запустили GUI-exe через mpiexec → mpiexec поднял 2 независимых процесса, и каждый создал свою форму. При нажатии кнопки код в одном процессе заходит в ветку N==2
и ждёт обменов через world
со «соседом», а соседский процесс в эту функцию вообще не заходил (он просто показывает свою форму). Отсюда взаимная блокировка/краш (SEHException
, exit code 0xc000001d
), потому что протокол обмена нарушен.
Чтобы MPI не влиял на GUI и при этом дал ускорение, разделите роли: GUI отдельно, MPI-воркеры отдельно.
GUI должен стартовать как обычное приложение (один процесс), без mpiexec. MPI-процессы запускаются отдельно и «без окон».
Есть два удобных варианта архитектуры.
В main()
вашего EXE:
--worker
(или переменная среды вроде PMI_RANK
), не создавайте форму: запустите «головную» функцию, которая вызывает ваш экспорт из DLL и выполняет обмены MPI.Тогда пользователь запускает GUI как обычно, а GUI при расчёте делает, например:
cpp// скрытно стартуем воркеров CreateProcessW(L"mpiexec.exe", L"-n 2 D:\\TEST_AGP\\x64\\Release\\TEST_AGP.exe --worker ...", ...); // ждём завершения / читаем пайп/файл с результатом
А воркеры (те же TEST_AGP.exe --worker
) не создают форму, лишь грузят DLL и зовут Base_LNA_1_2_Mer_AGP(...)
.
Обмен GUI↔воркеры — через named pipe / TCP-сокет / memory-mapped file / временный файл (на старте GUI передаёт путь/имя канала воркерам в командной строке).
Плюсы: один бинарник, минимум изменений в сборке. Минусы: нужно добавить простой IPC.
Соберите маленький консольный Worker.exe
, который:
Base_LNA_1_2_Mer_AGP
,GUI запускает:
mpiexec -n 2 Worker.exe --args...
и получает результат через выбранный IPC.
Плюсы: ещё чище разделение. Минусы: второй проект.
В обоих вариантах GUI никогда не должен быть внутри мультипроцессного мира MPI. Только worker-процессы.
Сейчас у вас в dllmain.cpp
:
cppconst boost::mpi::environment mpi_env; // глобальный объект extern const boost::mpi::communicator world; const boost::mpi::communicator world;
Замечания:
Init()
/RAII-объект, который создаётся в worker-процессе до первого использования MPI, а в GUI — либо не создаётся вовсе, либо создаётся с size==1
.environment
создаст коммуникатор с size==1
. Это ок — но не заходите в 2D-ветку, если world.size() < 2
.size==2
— всё нормально.Минимум защиты внутри экспортируемой функции:
cppif (N != 1.0f) { if (world.size() < 2) { // Вернуть код ошибки/кинуть исключение с понятным сообщением // либо fallback в однопроцессный расчёт return; } }
Инициализацию кривых Пеано в DllMain
тоже лучше не делать (в DllMain
запрещены многие действия). Перенесите создание Curve/Curve_Inverted
в безопасное место — например, в первый вход Base_LNA_1_2_Mer_AGP
под guard’ом:
cppstatic std::once_flag init_flag; std::call_once(init_flag, []{ int rank = world.rank(); if (rank == 0) Curve = std::make_unique<PeanoCurve_2D>(...); if (rank == 1) Curve_Inverted = std::make_unique<PeanoCurve_2D>(...); });
mpiexec -n 2
. Каждый процесс создаёт форму → две формы.Base_LNA_1_2_Mer_AGP(N=2)
, в котором есть sendrecv
и обмены с парой. Пара не вошла в функцию (вторая форма ждёт клика), протокол обмена нарушен → зависание/краш.LoadLibrary/GetProcAddress
; если f == nullptr
, вызов тоже даст SEHException
.После загрузки DLL проверьте указатели:
cpphLib = LoadLibrary(L"TEST_FUNC.dll"); if (!hLib) { MessageBox::Show("Не удалось загрузить TEST_FUNC.dll"); return; } f = (agp_c)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP"); ffree = (agp_free)GetProcAddress(hLib, "Base_LNA_1_2_Mer_AGP_Free"); if (!f || !ffree) { MessageBox::Show("Не найдены экспортируемые функции"); return; }
Совместимость соглашения вызова: у вас
cppextern "C" __declspec(dllexport) void Base_LNA_1_2_Mer_AGP(...); extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float*);
В typedef для ffree
указано typedef void(*agp_free)(float*);
— это по умолчанию __cdecl
, совпадает. Для agp_c
тоже __cdecl
по умолчанию — совпадает. Ок, но лучше явно:
cpptypedef void (__cdecl *agp_c)(...); typedef void (__cdecl *agp_free)(float*);
Защита от «не тот размер мира»:
cppif (N == 2.0f && world.size() < 2) { // верните пустой буфер и out_len=0 или бросьте читаемое исключение }
Не храните boost::mpi::communicator world;
как const
глобально, если планируете динамические спавны/отделение — оставьте гибкость.
В GUI не вызывайте MPI-код из UI-потока. Даже если уйдёте от mpiexec, длинные вызовы → в фоновый std::thread
/Task
, а в UI дергайте Invoke
.
main()
добавьте разбор аргументов:cppint main(int argc, char** argv) { bool worker = std::any_of(argv, argv+argc, [](char* s){ return std::string(s) == "--worker"; }); if (worker) { // headless: читаем параметры из argv/пайпа, зовём DLL, пишем результат и выходим return RunWorker(); } else { // обычный WinForms GUI Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); Application::Run(gcnew TESTAGP::MyForm()); } }
В button1_Click
:
\\\\.\\pipe\\agp_<GUID>
).mpiexec -n 2 TEST_AGP.exe --worker --pipe \\.\pipe\agp_<GUID>
.float
и длину, как раньше из DLL.В RunWorker()
:
Base_LNA_1_2_Mer_AGP(...)
.В DLL:
DllMain
. Используйте std::once_flag
для ленивой инициализации Curve/...
при первом заходе в функцию.world.size()
.Так вы:
Если хотите, могу быстро накидать пример минимального Worker.exe и кода named pipe (клиент/сервер) под ваши сигнатуры.