мой код в winforms.h : ref class MyForm : public ...

Criado em: 18 de setembro de 2025

Respondido usando GPT-5 por Chat01

Pergunta

мой код в 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);
}

text
protected: ~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;

text
std::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;
}
}
}

text
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) {
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;

text
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;

text
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); // достаточно одного 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

Pensamento

Providing WinForms app guidance

It looks like we need to solve an issue where launching a WinForms app with MPI results in multiple processes, each with its own GUI window. A good approach might be separating the GUI from MPI worker processes. One idea is to run a separate executable for workers, using pipes or sockets for communication, or designing a lightweight bootstrapper executable to handle computation without the UI.

Recommending solution for MPI and GUI

It seems MS-MPI likely supports MPI_Comm_spawn, though dynamic process functions can sometimes be disabled. The issue here is how to prevent launching the GUI under MPI. A good solution might be separating the GUI from worker processes: one executable for the GUI (no MPI) and another for the headless worker using the same DLL for computations.

Considering MPI and GUI communication

To avoid launching MPI processes within the GUI, I’m considering using IPC methods like shared memory or sockets. Alternatively, the GUI could call the DLL directly to start MPI worker processes. However, processes generally need mpiexec, and Boost.MPI’s dynamic features are unclear. A simpler solution might be running the GUI independently, using environment variables to trigger MPI initialization within the DLL only if needed.

Designing MPI-based architecture

MPI needs multiple processes for speed improvement, so here's the plan: the GUI runs as a single process, and on a button click, it launches a worker via mpiexec -n 2 Worker.exe (hid behind a window). The worker runs parallel tasks and communicates via shared memory or pipes. Afterward, the GUI loads results. There may be a crash from mismatched thread handling or calling conventions during function execution.

Proposing solutions for MPI integration

MPI requires multiple processes for performance. A key issue is that GUI processes may cause deadlocks when running MPI communications. The solution could be to decouple the MPI worker processes from the GUI. Three options are proposed:

Option A: Separate GUI from workers, starting workers using mpiexec.
Option B: Use dynamic MPI spawning in a single program.
Option C: Consider threads and OpenMP or similar for managing computation. Either way, making sure only workers handle MPI while the GUI remains separate is crucial.

MPI initialization and function pointer safety

The main concern is avoiding function pointer

Thought for 1m 32s

Resposta

Коротко: вы запустили GUI-exe через mpiexec → mpiexec поднял 2 независимых процесса, и каждый создал свою форму. При нажатии кнопки код в одном процессе заходит в ветку N==2 и ждёт обменов через world со «соседом», а соседский процесс в эту функцию вообще не заходил (он просто показывает свою форму). Отсюда взаимная блокировка/краш (SEHException, exit code 0xc000001d), потому что протокол обмена нарушен.
Чтобы MPI не влиял на GUI и при этом дал ускорение, разделите роли: GUI отдельно, MPI-воркеры отдельно.


Что сделать

1) Не запускать GUI через mpiexec

GUI должен стартовать как обычное приложение (один процесс), без mpiexec. MPI-процессы запускаются отдельно и «без окон».

Есть два удобных варианта архитектуры.

Вариант A (самый простой на Windows): один EXE — два режима

В main() вашего EXE:

  • Если есть флаг --worker (или переменная среды вроде PMI_RANK), не создавайте форму: запустите «головную» функцию, которая вызывает ваш экспорт из DLL и выполняет обмены MPI.
  • Если флага нет — обычный GUI.

Тогда пользователь запускает 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.

Вариант B: отдельный Worker.exe

Соберите маленький консольный Worker.exe, который:

  • загружает вашу DLL (или линкуется с ней),
  • парсит параметры (a,b,c,d,r, epsilon, seed…),
  • вызывает Base_LNA_1_2_Mer_AGP,
  • пишет результат в пайп/файл/сокет и завершает работу.

GUI запускает:

mpiexec -n 2 Worker.exe --args...

и получает результат через выбранный IPC.

Плюсы: ещё чище разделение. Минусы: второй проект.

В обоих вариантах GUI никогда не должен быть внутри мультипроцессного мира MPI. Только worker-процессы.


2) Сохранить MPI-инициализацию «при загрузке DLL», но безопасно

Сейчас у вас в dllmain.cpp:

cpp
const boost::mpi::environment mpi_env; // глобальный объект extern const boost::mpi::communicator world; const boost::mpi::communicator world;

Замечания:

  • Инициализировать тяжёлые подсистемы (MPI, выделения памяти, потоки) в DllMain/на этапе загрузки — рискованно на Windows. Лучше сделать явный Init()/RAII-объект, который создаётся в worker-процессе до первого использования MPI, а в GUI — либо не создаётся вовсе, либо создаётся с size==1.
  • Если очень хочется оставить, проверьте безопасный путь:
    • В GUI-процессе (без mpiexec) environment создаст коммуникатор с size==1. Это ок — но не заходите в 2D-ветку, если world.size() < 2.
    • В worker-процессах под mpiexec size==2 — всё нормально.

Минимум защиты внутри экспортируемой функции:

cpp
if (N != 1.0f) { if (world.size() < 2) { // Вернуть код ошибки/кинуть исключение с понятным сообщением // либо fallback в однопроцессный расчёт return; } }

Инициализацию кривых Пеано в DllMain тоже лучше не делать (в DllMain запрещены многие действия). Перенесите создание Curve/Curve_Inverted в безопасное место — например, в первый вход Base_LNA_1_2_Mer_AGP под guard’ом:

cpp
static 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>(...); });

3) Почему сейчас падает (разбор по вашим кускам)

  • Вы запускаете GUI.exe через mpiexec -n 2. Каждый процесс создаёт форму → две формы.
  • Нажимаете кнопку в одной форме → только один процесс вызывает Base_LNA_1_2_Mer_AGP(N=2), в котором есть sendrecv и обмены с парой. Пара не вошла в функцию (вторая форма ждёт клика), протокол обмена нарушен → зависание/краш.
  • Плюс у вас нет проверок после LoadLibrary/GetProcAddress; если f == nullptr, вызов тоже даст SEHException.

4) Технические мелочи, которые стоит поправить

  • После загрузки DLL проверьте указатели:

    cpp
    hLib = 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; }
  • Совместимость соглашения вызова: у вас

    cpp
    extern "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 по умолчанию — совпадает. Ок, но лучше явно:

    cpp
    typedef void (__cdecl *agp_c)(...); typedef void (__cdecl *agp_free)(float*);
  • Защита от «не тот размер мира»:

    cpp
    if (N == 2.0f && world.size() < 2) { // верните пустой буфер и out_len=0 или бросьте читаемое исключение }
  • Не храните boost::mpi::communicator world; как const глобально, если планируете динамические спавны/отделение — оставьте гибкость.

  • В GUI не вызывайте MPI-код из UI-потока. Даже если уйдёте от mpiexec, длинные вызовы → в фоновый std::thread/Task, а в UI дергайте Invoke.


5) Быстрый план внедрения (практика)

  1. В main() добавьте разбор аргументов:
cpp
int 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()); } }
  1. В button1_Click:

    • Сериализуйте параметры в JSON/строку.
    • Создайте named pipe (к примеру, \\\\.\\pipe\\agp_<GUID>).
    • Запустите mpiexec -n 2 TEST_AGP.exe --worker --pipe \\.\pipe\agp_<GUID>.
    • Примите через пайп массив float и длину, как раньше из DLL.
  2. В RunWorker():

    • Подключитесь к пайпу/прочтите argv.
    • Вызовите Base_LNA_1_2_Mer_AGP(...).
    • Отправьте буфер в пайп, завершите процесс.
  3. В DLL:

    • Уберите инициализацию тяжёлых вещей из DllMain. Используйте std::once_flag для ленивой инициализации Curve/... при первом заходе в функцию.
    • Добавьте проверки world.size().

Так вы:

  • сохраните максимальный выигрыш от MPI (действительно будет 2+ процесса),
  • полностью развяжете GUI от мира MPI,
  • избежите двух окон и крашей при обменах,
  • оставите желаемую «инициализацию при загрузке» по сути (ленивая в воркерах; в GUI — безвредная).

Если хотите, могу быстро накидать пример минимального Worker.exe и кода named pipe (клиент/сервер) под ваши сигнатуры.

Compartilhar esta Q&A