std::vector<float> TESTAGP::MyForm::Base_LNA_1_2_M...
बनाया गया: 14 अगस्त 2025
का उपयोग करके उत्तर दिया गया GPT-5 Thinking द्वारा Chat01
बनाया गया: 14 अगस्त 2025
का उपयोग करके उत्तर दिया गया GPT-5 Thinking द्वारा Chat01
std::vector<float> TESTAGP::MyForm::Base_LNA_1_2_Mer_AGP(
time_t now, bool mode, unsigned short N, float a, float b,
PeanoCurve_2D ^ Curve, PeanoCurve_2D ^ Curve_Minus_PI_Na_Dva,
unsigned short r, float epsilon, unsigned short global_iterations, float c,
float d) {
unsigned short schetchick = 0;
if (N == 1) {
// Кеширование параметров и флагов
const unsigned short r_val = r;
const unsigned short global_iterations_val = global_iterations;
const bool use_mode = mode;
const float initial_length = b - a;
const float epsilon_val = epsilon;
text// Переменные, специфичные для одномерного случая std::pair<float, float> start, end; std::pair<float, float> x_Rmax, y_Rmax; std::pair<float, float> pred_i_sled_shag; std::pair<float, float> promejutochnaya_tochka; float Mmax, m; float dmax = 0.0f, eta_shtrih = 0.0f; std::vector<Interval> R, R1; std::vector<float> Extr; HINSTANCE load_function = LoadLibrary(L"TEST_FUNC.dll"); typedef float (*sh)(float, time_t); sh ShekelFunc = (sh)GetProcAddress(load_function, "ShekelFunc"); float best_x = a; float best_f = ShekelFunc(a, now); // Инициализация с кешированием start = std::make_pair(a, best_f); end = std::make_pair(b, ShekelFunc(b, now)); Interval nachalny_otrezok(start, end, N); Mmax = nachalny_otrezok.M; m = r_val * Mmax; x_Rmax = std::make_pair(start.first, end.first); y_Rmax = std::make_pair(start.second, end.second); // Исправление: резервирование без запаса R.reserve(global_iterations_val); R.push_back(std::move(nachalny_otrezok)); std::make_heap(R.begin(), R.end(), Compare{}); pred_i_sled_shag = std::make_pair(a, b); // Основной цикл оптимизирован для работы с кешем while (true) { // Кеширование координат для вычислений const float x1 = x_Rmax.first; const float x2 = x_Rmax.second; const float y1 = y_Rmax.first; const float y2 = y_Rmax.second; // Вычисление новой точки pred_i_sled_shag.first = pred_i_sled_shag.second; const float new_point = Shag(m, x1, x2, y1, y2, N, r_val); pred_i_sled_shag.second = new_point; promejutochnaya_tochka.first = new_point; // Кеширование значения функции const float new_value = ShekelFunc(new_point, now); promejutochnaya_tochka.second = new_value; // ВИЗУАЛИЗАЦИЯ С УЧЁТОМ РЕЖИМА if (use_mode) { chart2->Series[1]->Points->AddXY(new_point, new_value); } else { chart2->Series[0]->Points->AddXY(new_point, new_value); } // Обновление лучшей точки if (new_value < best_f) { best_f = new_value; best_x = new_point; Extr.push_back(best_f); } // Кеширование счетчика для частых проверок const unsigned short current_counter = schetchick; // Проверка на завершение по итерациям if (current_counter == global_iterations_val) { const Interval& front = R.front(); const float interval_size = fabsf(front.end.first - front.start.first); // ВЫВОД ФИНАЛЬНОЙ ТОЧКИ В ЗАВИСИМОСТИ ОТ РЕЖИМА if (use_mode) { chart2->Series[3]->Points->AddXY(best_x, best_f); } else { chart2->Series[2]->Points->AddXY(best_x, best_f); } // ВЫВОД ИТЕРАЦИЙ В РАЗНЫЕ ПОЛЯ В ЗАВИСИМОСТИ ОТ РЕЖИМА if (use_mode) { textBox8->Text = Convert::ToString( current_counter); // Локальный режим -> textBox8 } else { textBox6->Text = Convert::ToString(current_counter); // Обычный режим -> textBox6 } textBox7->Text = Convert::ToString(interval_size); textBox4->Text = Convert::ToString(best_x); return Extr; } // Извлечение интервала с оптимизацией перемещения std::pop_heap(R.begin(), R.end(), Compare{}); Interval promejutochny_otrezok = std::move(R.back()); R.pop_back(); // Создание новых интервалов с кешированием точек const auto& start_pt = promejutochny_otrezok.start; const auto& mid_pt = promejutochnaya_tochka; const auto& end_pt = promejutochny_otrezok.end; Interval curr(start_pt, mid_pt, N); Interval curr1(mid_pt, end_pt, N); if (use_mode) { // Кеширование длин интервалов const float len1 = curr.end.first - curr.start.first; const float len2 = curr1.end.first - curr1.start.first; float new_dmax = max(len1, len2);
// Параллельный поиск максимальной длины
#pragma omp parallel for reduction(max : new_dmax)
for (int i = 0; i < R.size(); i++) {
const auto& item = R[i];
const float len = item.end.first - item.start.first;
if (len > new_dmax) new_dmax = len;
}
dmax = new_dmax;
text// Обработка режима с акцентом на локальность данных if (dmax * 10 < 3 * initial_length && current_counter % 3 == 0 || dmax < 0.1f * initial_length) { // Кеширование характеристик интервалов const float currM = max(curr.M, curr1.M); if (currM > Mmax || best_f == new_value) { Mmax = currM; m = r_val * Mmax; } const float global_coeff = (r_val - 1) * Mmax * (1.0f / dmax); const float progress = 1.0f - dmax / (0.3f * initial_length); const float alpha = 1.0f + progress * progress; const float betta = 2.0f - alpha; curr.ChangeCharacteristic( alpha * curr.M + betta * global_coeff * len1, N); curr1.ChangeCharacteristic( alpha * curr1.M + betta * global_coeff * len2, N);
// Параллельная обработка вектора
#pragma omp parallel for
for (int i = 0; i < R.size(); i++) {
auto& item = R[i];
const float len_item = item.end.first - item.start.first;
textitem.ChangeCharacteristic( alpha * item.M + betta * global_coeff * len_item, N); // Исправление: вызов функции } std::make_heap(R.begin(), R.end(), Compare{}); } else { // Обработка нережимного случая const float currM = max(curr.M, curr1.M); if (currM < Mmax && best_f != new_value) { curr.ChangeCharacteristic(m, N); curr1.ChangeCharacteristic(m, N); } else { Mmax = currM; m = r_val * Mmax; curr.ChangeCharacteristic(m, N); curr1.ChangeCharacteristic(m, N);
// Параллельное обновление характеристик
#pragma omp parallel for
for (int i = 0; i < R.size(); i++) {
R[i].ChangeCharacteristic(m, N); // Исправление: вызов функции
}
std::make_heap(R.begin(), R.end(), Compare{});
}
}
} else {
// Обработка нережимного случая
const float currM = max(curr.M, curr1.M);
textif (currM < Mmax && best_f != new_value) { curr.ChangeCharacteristic(m, N); curr1.ChangeCharacteristic(m, N); } else { Mmax = currM; m = r_val * Mmax; curr.ChangeCharacteristic(m, N); curr1.ChangeCharacteristic(m, N);
// Параллельное обновление характеристик
#pragma omp parallel for
for (int i = 0; i < R.size(); i++) {
R[i].ChangeCharacteristic(m, N); // Исправление: вызов функции
}
std::make_heap(R.begin(), R.end(), Compare{});
}
}
text// Добавление новых интервалов R.push_back(std::move(curr)); std::push_heap(R.begin(), R.end(), Compare{}); R.push_back(std::move(curr1)); std::push_heap(R.begin(), R.end(), Compare{}); // Кеширование топ-элемента const Interval& top = R.front(); const float interval_length = fabsf(top.end.first - top.start.first); // Проверка условия останова if (interval_length < epsilon_val) { // ВЫВОД ФИНАЛЬНОЙ ТОЧКИ В ЗАВИСИМОСТИ ОТ РЕЖИМА if (use_mode) { chart2->Series[3]->Points->AddXY(best_x, best_f); } else { chart2->Series[2]->Points->AddXY(best_x, best_f); } // ВЫВОД ИТЕРАЦИЙ В РАЗНЫЕ ПОЛЯ В ЗАВИСИМОСТИ ОТ РЕЖИМА if (use_mode) { textBox8->Text = Convert::ToString( current_counter); // Локальный режим -> textBox8 } else { textBox6->Text = Convert::ToString(current_counter); // Обычный режим -> textBox6 } textBox7->Text = Convert::ToString(interval_length); textBox4->Text = Convert::ToString(best_x); return Extr; } // Обновление границ для следующей итерации x_Rmax = {top.start.first, top.end.first}; y_Rmax = {top.start.second, top.end.second}; schetchick++; }
} моя одномерная ветка одномерного алгоритма глобального поиска - она уже довольно хорошо оптимизирована и сам алгоритм верен, сам алгоритм оптимизирован, но возможно добавить микрооптимизации в сам код (реализацию алгоритма а не сам алгоритм) по скорости - добавь микрооптимизации для максимальной скорости, например где-то возможно улучшить кеширование (хотя избегай некоторых схем кеширования, которые могли бы сработать в нативном коде) - потому что этот код в управляемом коде, вызов DLL библиотек также оптимизирован, возможно можно оптимизировать UI - вывод самих точек, код в c++/cll проекте WinForms NetFramework (не просто Net проект а именно устаревший формат). Возможно можно где-то добавить ещё параллелизма, вектор интервалов и правда может быть довольно большим (вплоть до нескольких тысяч элементов), не думай про вызываемые в этом коде мои функции - они быстрые и инлайнятся. Ты можешь активно использовать диррективы из OpenMP там где это оправдано, имей также ввиду что несмотря на большой размер очередей сама программа выполняется быстро - несколько секунд (не больше 4-5) в худшем случае, доли секунд (сотые возможно даже тысячные доли в лучшем случае) - так что все вычисления выполняются на процессоре - так что кеширование действительно имеет смысл - НО КЕШИРОВАТЬ ЗНАЧЕНИЯ ФУНКЦИИ ДУМАЯ ЧТО ОНИ МОГУТ ПОВТОРНО ИСПОЛЬЗОВАТЬСЯ В СЛЕДУЮЩИХ ИТЕРАЦИЯХ НЕЛЬЗЯ - КАЖДУЮ ИТЕРАЦИЮ ФУНКЦИЯ ПЕРЕСЧИТЫВАЕТСЯ В НОВОЙ ТОЧКЕ - ОНИ МОГУТ БЫТЬ ОТНОСИТЕЛЬНО БЛИЗКИ НО НИКОГДА НЕ СОВПАДУТ, также имей ввиду что я использую компилятор MSVC
крутой кусок! сам алгоритм у тебя уже «горячий», так что выигрыш сейчас в основном из-за уменьшения накладных расходов: меньше переходов между управляемым и нативным миром, меньше перерасчётов всей кучи, меньше ветвлений в горячем цикле, аккуратный OpenMP и аккуратный UI. Ниже — конкретные правки в духе «микро», которые обычно дают ощутимые проценты на MSVC в C++/CLI WinForms.
UI-вызовы в каждой итерации — самый дорогой путь. Переносим их в буфер и флашим пачками, заранее выключив перерисовку.
Ветвление по mode
в каждой итерации — устраняем; заранее выбираем нужные коллекции и текстбоксы.
Многократные полные пересчёты dmax
и make_heap
— пересчитываем только при необходимости; остальное поддерживаем инкрементально.
Загрузка DLL и получение указателя на функцию — один раз на процесс, без повторных LoadLibrary/GetProcAddress
.
Лишние деления/fabsf
/повторные вычисления — предвычисляем постоянные, держим «обратные» величины.
OpenMP — добавляем schedule(static)
, пороги включения, и simd
там, где компилятор может векторизовать; избегаем тривиального оверхеда на маленьких размерах R
.
Выделения в векторах — reserve
везде, где растём, emplace_back
вместо push_back(std::move(...))
.
Работа с кучей — откладываем полный make_heap
до момента, когда он действительно нужен («грязный» флаг).
Типы и вызовы — избегаем max
из Windows-макроса, используем std::max
; индексы циклов — int
/ptrdiff_t
с явным приведением.
Ниже — вставки/замены «кусок-в-кусок». Они не меняют логику алгоритма, только реализацию.
cpp// В .cpp, вне функции (один раз на модуль) struct ShekelFuncHolder { using Fn = float (__cdecl*)(float, time_t); HINSTANCE h = nullptr; Fn fn = nullptr; ShekelFuncHolder() noexcept { h = GetModuleHandleW(L"TEST_FUNC.dll"); if (!h) h = LoadLibraryW(L"TEST_FUNC.dll"); fn = reinterpret_cast<Fn>(GetProcAddress(h, "ShekelFunc")); // Ничего не выгружаем: повторные вызовы будут быстрыми и безопасными. } }; static ShekelFuncHolder g_shekel; // lazy-init при первом обращении
А в функции вместо LoadLibrary/GetProcAddress
:
cppauto ShekelFunc = g_shekel.fn; // уже готовый быстрый указатель
cpp// перед циклом auto^ iterBox = use_mode ? textBox8 : textBox6; auto^ lenBox = textBox7; auto^ xBestBox= textBox4; auto^ seriesPoints = chart2->Series[use_mode ? 1 : 0]->Points; auto^ seriesBest = chart2->Series[use_mode ? 3 : 2]->Points; // буферизация точек и подавление лишних перерисовок chart2->SuspendLayout(); // вернём позже System::Collections::Generic::List< System::Windows::Forms::DataVisualization::Charting::DataPoint^>^ buf = gcnew System::Collections::Generic::List< System::Windows::Forms::DataVisualization::Charting::DataPoint^>(1024); const int UI_STEP = 16; // подобрать: 8–64 — обычно идеально int ui_count = 0; auto add_point_buffered = [&](double x, double y) { // НЕ добавляем каждую итерацию напрямую buf->Add(gcnew System::Windows::Forms::DataVisualization::Charting::DataPoint(x, y)); if ((++ui_count & (UI_STEP - 1)) == 0) { // степень двойки => дешёвый mod array<...::DataPoint^>^ arr = buf->ToArray(); seriesPoints->AddRange(arr); buf->Clear(); } };
В цикле теперь:
cppadd_point_buffered(new_point, new_value);
А при любом «финальном» выводе (выход по итерациям или по epsilon
) обязательно «доливаем» остаток и показываем лучшую точку одной операцией:
cppif (buf->Count) { seriesPoints->AddRange(buf->ToArray()); buf->Clear(); } seriesBest->AddXY(best_x, best_f); iterBox->Text = Convert::ToString(current_counter); lenBox->Text = Convert::ToString(interval_size /*или interval_length*/); xBestBox->Text= Convert::ToString(best_x); chart2->ResumeLayout(); // включили обратно
Если нужен ещё больший выигрыш — при вычислениях вообще не трогать
chart2
, а только на выходе: но это уже вопрос UX. Твой текущий вариант с «дросселем» даёт 10-100× меньше вызовов в UI.
Весь код вида:
cppif (use_mode) { ... } else { ... }
внутри самого цикла можно свести к использованию уже выбранных seriesPoints/seriesBest/iterBox
. Это убирает постоянные условные переходы из «горячей» части.
dmax
зависит только от длин интервалов. Эти длины у уже существующих интервалов не меняются, меняются только у двух новых при разбиении. Значит:
dmax
инкрементально: dmax = std::max(dmax, std::max(len1, len2))
.dmax
, ставим флаг dmax_dirty = true
и лишь изредка (или когда этот флаг поднят) делаем полный проход по R
, иначе — не трогаем.ChangeCharacteristic(...)
не делаем make_heap
немедленно. Помечаем heap_dirty = true
и выполняем make_heap
ровно перед следующей операцией pop_heap
(в начале итерации). Это один make_heap
вместо 2-3.Пример вставок:
cppbool heap_dirty = false; bool dmax_dirty = false; // ... после pop_heap мы знаем "top" const auto popped_len = std::fabs(top.end.first - top.start.first); if (popped_len == dmax) dmax_dirty = true; // при создании новых интервалов: const float len1 = curr.end.first - curr.start.first; const float len2 = curr1.end.first - curr1.start.first; dmax = std::max(dmax, std::max(len1, len2)); // только когда действительно нужно точное dmax: if (dmax_dirty && ((current_counter & 3) == 0)) { float new_dmax = std::max(len1, len2); const int n = static_cast<int>(R.size()); #pragma omp parallel for reduction(max:new_dmax) schedule(static) if(n > 512) for (int i = 0; i < n; ++i) { const float li = R[i].end.first - R[i].start.first; if (li > new_dmax) new_dmax = li; } dmax = new_dmax; dmax_dirty = false; }
И перед извлечением вершины (в начале шага):
cppif (heap_dirty) { std::make_heap(R.begin(), R.end(), Compare{}); heap_dirty = false; } // затем обычный pop_heap / push_heap
А там, где раньше делался мгновенный std::make_heap(...)
после массовых обновлений:
cppheap_dirty = true; // вместо make_heap прямо сейчас
cppconst float initial_length = b - a; const float inv_initial_len = 1.0f / initial_length; const float inv_03_initial = 1.0f / (0.3f * initial_length); // ... // было: const float progress = 1.0f - dmax / (0.3f * initial_length); const float progress = 1.0f - dmax * inv_03_initial;
R
параллель — чистый оверхед. Добавляем условие if(n > порог)
.schedule(static)
— для равномерных лёгких тел цикла это дешевле по оверхеду.#pragma omp simd
(MSVC поддерживает).Пример:
cppconst int n = static_cast<int>(R.size()); #pragma omp parallel for schedule(static) if(n > 512) for (int i = 0; i < n; ++i) { auto& item = R[i]; const float len_item = item.end.first - item.start.first; item.ChangeCharacteristic(alpha * item.M + betta * global_coeff * len_item, N); } heap_dirty = true; // отложим make_heap
AddXY(float,float)
всё равно идёт упаковка в double
; сразу передавай double
(как выше) — упрощаем преобразования.Extr
: заранее резерв:
cppExtr.reserve(std::min<unsigned>(global_iterations_val, 1024u));
fabsf
на длину интервала → у тебя везде end.first >= start.first
(если так гарантируется) — тогда модуль не нужен; если нет — оставь, но можно заменить на условие:
cppfloat interval_size = front.end.first - front.start.first; if (interval_size < 0) interval_size = -interval_size;
max
: обязательно std::max
, чтобы не пойматься на #define max
из <windows.h>
.for (int i = 0; i < R.size(); ++i)
— вынести const int n = (int)R.size();
вверх, чтобы не читать size()
каждый раз, и явное приведение типов.push_back(std::move(x))
→ emplace_back(std::move(x))
— на практике одинаково, но emplace_back
честнее намерением и иногда короче путь.AddXY
) → десятки миллисекунд экономии только на UI.make_heap
→ экономия O(n) на каждой итерации, когда раньше ты перестраивал кучу полностью.dmax
→ нет O(n) сканов на каждом шаге.R
.Если захочешь выжать ещё:
while(true)
без UI) в блок #pragma managed(push, off)
→ получается полностью нативный горячий код, а UI обновлять только по окончании либо редкими «снимками». В C++/CLI это порой даёт двузначные проценты на тех же MSVC /clr.Interval
хранить готовую длину length
(и обновлять только при конструировании/разбиении). Тогда в любых местах длина читается без вычитания./O2 /GL /GS- /fp:fast /arch:AVX2
(или под твой CPU), OpenMP Support: Yes
, для C++/CLI иногда помогает -d2SSAOptimizer-
убрать, но это уже тонкий тюнинг.Если хочешь, могу прислать аккуратный патч твоего метода целиком (с буферизацией UI, «грязной» кучей и инкрементальным dmax
) — вставишь «as is».