#include "pch.h" #define XOR_RAND(state, result_v...

Создано: 10 октября 2025 г.

Отвечено с использованием GPT-5 Thinking от Chat01

Вопрос

#include "pch.h"

#define XOR_RAND(state, result_var)
do {
int s = state;
s ^= s << 13;
s ^= s >> 17;
s ^= s << 5;
state = s;
result_var = state * 0x1.0p-32f;
} while (0)

#define XOR_RAND_GRSH(state, result_var)
do {
int s = state;
s ^= s << 13;
s ^= s >> 17;
s ^= s << 5;
state = s;
result_var = fmaf(state, 0x1.0p-31f, -1.0f);
} while (0)

#define FABE13_COS(x, result_var)
do {
float abs_val = fabsf(x);
float reduced = fmodf(abs_val, 6.28318530718f);
if (reduced > 3.14159265359f) {
reduced = 6.28318530718f - reduced;
}
if (reduced < 1.57079632679f) {
float val2 = reduced * reduced;
result_var = fmaf(val2, fmaf(val2, 0.0416666667f, -0.5f), 1.0f);
} else {
reduced = 3.14159265359f - reduced;
float val2 = reduced * reduced;
result_var = -fmaf(val2, fmaf(val2, 0.0416666667f, -0.5f), 1.0f);
}
} while (0)

#define FABE13_SINCOS(in, sin_out, cos_out, n)
do {
int i = 0;
int limit = n & ~7;
if (n >= 8) {
static __declspec(align(32)) const __m256 VEC_TWOPI = _mm256_set1_ps(6.28318530718f);
static __declspec(align(32)) const __m256 VEC_PI = _mm256_set1_ps(3.14159265359f);
static __declspec(align(32)) const __m256 VEC_PI_2 = _mm256_set1_ps(1.57079632679f);
static __declspec(align(32)) const __m256 INV_TWOPI = _mm256_set1_ps(0.15915494309189535f);
static __declspec(align(32)) const __m256 BIAS = _mm256_set1_ps(12582912.0f);
static __declspec(align(32)) const __m256 VEC_COS_P3 = _mm256_set1_ps(0.0416666667f);
static __declspec(align(32)) const __m256 VEC_COS_P1 = _mm256_set1_ps(-0.5f);
static __declspec(align(32)) const __m256 VEC_COS_P0 = _mm256_set1_ps(1.0f);
static __declspec(align(32)) const __m256 VEC_SIN_P1 = _mm256_set1_ps(-0.16666666f);
static __declspec(align(32)) const __m256 VEC_SIN_P0 = _mm256_set1_ps(1.0f);
static __declspec(align(32)) const __m256 VEC_ZERO = _mm256_setzero_ps();
while (i < limit) {
__m256 vx = _mm256_load_ps(&in[i]);
__m256 vax = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), vx);
__m256 q = _mm256_fmadd_ps(vax, INV_TWOPI, BIAS);
q = _mm256_sub_ps(q, BIAS);
__m256 r = _mm256_fnmadd_ps(VEC_TWOPI, q, vax);
__m256 r1 = _mm256_min_ps(r, _mm256_sub_ps(VEC_TWOPI, r));
__m256 r2 = _mm256_min_ps(r1, _mm256_sub_ps(VEC_PI, r1));
__m256 t2 = _mm256_mul_ps(r2, r2);
__m256 cosv = _mm256_fmadd_ps(t2, _mm256_fmadd_ps(t2, VEC_COS_P3, VEC_COS_P1), VEC_COS_P0);
__m256 sinv = _mm256_mul_ps(_mm256_fmadd_ps(t2, VEC_SIN_P1, VEC_SIN_P0), r2);
__m256 cflip = _mm256_cmp_ps(r1, VEC_PI_2, _CMP_GT_OQ);
__m256 sflip = _mm256_xor_ps(_mm256_cmp_ps(vx, VEC_ZERO, _CMP_LT_OQ),
_mm256_cmp_ps(r, VEC_PI, _CMP_GT_OQ));
cosv = _mm256_blendv_ps(cosv, _mm256_sub_ps(VEC_ZERO, cosv), cflip);
sinv = _mm256_blendv_ps(sinv, _mm256_sub_ps(VEC_ZERO, sinv), sflip);
_mm256_store_ps(&cos_out[i], cosv);
_mm256_store_ps(&sin_out[i], sinv);
i += 8;
}
}
while (i < n) {
float x = in[i];
float ax = fabsf(x);
float q = fmaf(ax, 0.15915494309189535f, 12582912.0f);
q -= 12582912.0f;
float r = fmaf(-6.28318530718f, q, ax);
bool sflip = (r > 3.14159265359f);
if (sflip) r = 6.28318530718f - r;
bool cflip = (r > 1.57079632679f);
if (cflip) r = 3.14159265359f - r;
float t2 = r * r;
float c = fmaf(t2, fmaf(t2, 0.0416666667f, -0.5f), 1.0f);
float s = fmaf(t2, -0.16666666f, 1.0f) * r;
cos_out[i] = cflip ? -c : c;
sin_out[i] = ((x < 0.0f) ^ sflip) ? -s : s;
++i;
}
} while (0)

static __declspec(noalias) __forceinline float ShekelFunc(const float x, const float seed) noexcept
{
int i = 0;
float current_state = seed, current_res, current_res2, res = 0.0f;
while (i < 10) {
XOR_RAND(current_state, current_res);
const float x_part = fmaf(-current_res, 10.0f, x);
XOR_RAND(current_state, current_res);
XOR_RAND(current_state, current_res2);
float delimiter = fmaf(fmaf(current_res, 20.0f, 5.0f),
x_part * x_part,
fmaf(current_res2, 0.2f, 1.0f));
delimiter = copysignf(fmaxf(fabsf(delimiter), FLT_MIN), delimiter);
res -= 1.0f / delimiter;
++i;
}
return res;
}

static __declspec(noalias) __forceinline float RastriginFunc(const float x1, const float x2) noexcept
{
const float term1 = fmaf(x1, x1, x2 * x2);
float cos1, cos2;
FABE13_COS(6.28318530717958647692f * x1, cos1);
FABE13_COS(6.28318530717958647692f * x2, cos2);
return (term1 - fmaf(cos1 + cos2, 10.0f, -14.6f)) * fmaf(-term1, 0.25f, 18.42f);
}

static __declspec(noalias) __forceinline float HillFunc(const float x, const float seed) noexcept
{
int j = 0;
static __declspec(align(32)) float angles[14u];
const float start_angle = 6.28318530717958647692f * x;
#pragma loop(ivdep)
while (j < 14) {
angles[j] = start_angle * (j + 1);
++j;
}
static __declspec(align(32)) float sin_vals[14u];
static __declspec(align(32)) float cos_vals[14u];
FABE13_SINCOS(angles, sin_vals, cos_vals, 14u);
float current_state = seed, current_res, current_res2;
XOR_RAND(current_state, current_res);
float res = fmaf(current_res, 2.0f, -1.1f);
--j;
while (j >= 0) {
XOR_RAND(current_state, current_res);
XOR_RAND(current_state, current_res2);
res += fmaf(fmaf(current_res, 2.0f, -1.1f), sin_vals[j],
fmaf(current_res, 2.0f, -1.1f) * cos_vals[j]);
--j;
}
return res;
}

static __declspec(noalias) __forceinline float GrishaginFunc(const float x1, const float x2, const float seed) noexcept
{
int j = 0;
static __declspec(align(32)) float angles_j[8u];
static __declspec(align(32)) float angles_k[8u];
#pragma loop(ivdep)
while (j < 8) {
const float pj_mult = 3.14159265358979323846f * (j + 1);
angles_j[j] = pj_mult * x1;
angles_k[j] = pj_mult * x2;
++j;
}
static __declspec(align(32)) float sin_j[8u], cos_j[8u];
static __declspec(align(32)) float sin_k[8u], cos_k[8u];
FABE13_SINCOS(angles_j, sin_j, cos_j, 8u);
FABE13_SINCOS(angles_k, sin_k, cos_k, 8u);
--j;
float part1 = 0.0f;
float part2 = 0.0f;
float current_state = seed, current_res, current_res2;
while (j >= 0) {
size_t k = 0u;
while (k < 8u) {
const float sin_term = sin_j[j] * sin_j[j];
const float cos_term = cos_k[k] * cos_k[k];
XOR_RAND_GRSH(current_state, current_res);
XOR_RAND_GRSH(current_state, current_res2);
part1 = fmaf(current_res, sin_term,
fmaf(current_res2, cos_term, part1));
XOR_RAND_GRSH(current_state, current_res);
XOR_RAND_GRSH(current_state, current_res2);
part2 = fmaf(-current_res, cos_term,
fmaf(current_res2, sin_term, part2));
++k;
}
--j;
}
return -sqrtf(fmaf(part1, part1, part2 * part2));
}

static __declspec(noalias) __forceinline float Shag(const float _m, const float x1, const float x2, const float y1,
const float y2, const float _N, const float _r) noexcept
{
const float diff = y2 - y1;
const float sign_mult = _mm_cvtss_f32(_mm_castsi128_ps(_mm_set1_epi32(
0x3F800000 | ((((const int)&diff) & 0x80000000) ^ 0x80000000)
)));
return _N == 1.0f
? fmaf(-(1.0f / _m), diff, x1 + x2) * 0.5f
: _N == 2.0f
? fmaf(sign_mult / (_m * _m), diff * diff * _r, x1 + x2) * 0.5f
: fmaf(sign_mult / powf(_m, _N), powf(diff, _N) * _r, x1 + x2) * 0.5f;
}

__declspec(align(64)) struct PreallocatedPeanoMemoryManager final {
__declspec(align(64)) struct MemoryBlock final {
char* __restrict data;
char* __restrict current;
const char* __restrict end;

text
__declspec(noalias) __forceinline MemoryBlock() noexcept : data(nullptr), current(nullptr), end(nullptr) { } const __declspec(noalias) __forceinline void initialize(const size_t bytes) noexcept { const size_t aligned_need = (bytes + 1048638) & ~(1048575); void* p = VirtualAlloc(nullptr, aligned_need, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); data = reinterpret_cast<char* __restrict>(p); const uintptr_t base = reinterpret_cast<uintptr_t>(data); const uintptr_t aligned = (base + 63) & ~63; current = reinterpret_cast<char* __restrict>(aligned); end = reinterpret_cast<char*>(current + bytes); } __declspec(noalias) __forceinline void* allocate_from_block(const size_t alloc_size) noexcept { const size_t n = (alloc_size + 63) & ~63; char* __restrict result = current; current += n; return result; } }; tbb::enumerable_thread_specific<MemoryBlock> tls; const __declspec(noalias) __forceinline size_t calculate_tree_size_bytes(const int max_depth) const noexcept { static const size_t bytes[] = { 1398080u, 5592384u, 22369600u, 89478464u, 357913920u, 1431655744u, 5726623040u }; return bytes[max_depth - 7u]; } const __declspec(noalias) __forceinline void preallocate_for_depth(const int max_depth) noexcept { const size_t required_size = calculate_tree_size_bytes(max_depth); tls.clear(); tls = tbb::enumerable_thread_specific<MemoryBlock>( [required_size]() { MemoryBlock b; b.initialize(required_size); return b; } ); } __declspec(noalias) __forceinline void* allocate(const size_t size) noexcept { return tls.local().allocate_from_block(size); }

};

static PreallocatedPeanoMemoryManager peano_memory_manager;

__declspec(align(16)) struct Interval final {
const float x1;
const float x2;
const float y1;
const float y2;
const float delta_y;
const float N_factor;
const float M;
float R;

text
__declspec(noalias) __forceinline void* operator new(size_t sz) noexcept { return peano_memory_manager.allocate(sz); } __declspec(noalias) __forceinline Interval(const float _x1, const float _x2, const float _y1, const float _y2, const float _N) noexcept : x1(_x1), x2(_x2), y1(_y1), y2(_y2), delta_y(_y2 - _y1), N_factor(_N == 1.0f ? _x2 - _x1 : _N == 2.0f ? sqrtf(_x2 - _x1) : powf(_x2 - _x1, 1.0f / _N)), M(fabsf(delta_y)* (1.0f / N_factor)) { } __declspec(noalias) __forceinline void ChangeCharacteristic(const float _m) noexcept { R = fmaf( -(y2 + y1), 2.0f, fmaf(_m, N_factor, (delta_y * delta_y) * (1.0f / (_m * N_factor)))); }

};

static __declspec(noalias) __forceinline bool ComparePtr(const Interval* __restrict a, const Interval* __restrict b) noexcept
{
return a->R < b->R;
}

const enum List : uint8_t {
Top = 0b00,
Down = 0b01,
Left = 0b10,
Right = 0b11
};

__declspec(align(16)) struct PeanoCurve_2D final {
const List Type;
const int razvertka;
const float a;
const float b;
const float c;
const float d;
const float x1;
const float x2;
PeanoCurve_2D* __restrict DownLeft;
PeanoCurve_2D* __restrict TopLeft;
PeanoCurve_2D* __restrict TopRight;
PeanoCurve_2D* __restrict DownRight;

text
__declspec(noalias) __forceinline void* operator new(size_t sz) noexcept { return peano_memory_manager.allocate(sz); } __declspec(noalias) __forceinline PeanoCurve_2D( const List _Type, int _razvertka, const float _a, const float _b, const float _c, const float _d) noexcept : Type(_Type), razvertka(_razvertka), a(_a), b(_b), c(_c), d(_d), x1(fmaf(0.5f, _a, 0.5f * _b)), x2(fmaf(0.5f, _c, 0.5f * _d)) { if (_razvertka-- != 0) { if (_razvertka > 8) { switch (_Type) { case Top: tbb::parallel_invoke( [&]() { DownLeft = new PeanoCurve_2D(Right, _razvertka, _a, x1, _c, x2); }, [&]() { TopLeft = new PeanoCurve_2D(Top, _razvertka, _a, x1, x2, _d); }, [&]() { TopRight = new PeanoCurve_2D(Top, _razvertka, x1, _b, x2, _d); }, [&]() { DownRight = new PeanoCurve_2D(Left, _razvertka, x1, _b, _c, x2); } ); break; case Down: tbb::parallel_invoke( [&]() { TopRight = new PeanoCurve_2D(Left, _razvertka, x1, _b, x2, _d); }, [&]() { DownRight = new PeanoCurve_2D(Down, _razvertka, x1, _b, _c, x2); }, [&]() { DownLeft = new PeanoCurve_2D(Down, _razvertka, _a, x1, _c, x2); }, [&]() { TopLeft = new PeanoCurve_2D(Right, _razvertka, _a, x1, x2, _d); } ); break; case Right: tbb::parallel_invoke( [&]() { DownLeft = new PeanoCurve_2D(Top, _razvertka, _a, x1, _c, x2); }, [&]() { DownRight = new PeanoCurve_2D(Right, _razvertka, x1, _b, _c, x2); }, [&]() { TopRight = new PeanoCurve_2D(Right, _razvertka, x1, _b, x2, _d); }, [&]() { TopLeft = new PeanoCurve_2D(Down, _razvertka, _a, x1, x2, _d); } ); break; default: tbb::parallel_invoke( [&]() { TopRight = new PeanoCurve_2D(Down, _razvertka, x1, _b, x2, _d); }, [&]() { TopLeft = new PeanoCurve_2D(Left, _razvertka, _a, x1, x2, _d); }, [&]() { DownLeft = new PeanoCurve_2D(Left, _razvertka, _a, x1, _c, x2); }, [&]() { DownRight = new PeanoCurve_2D(Top, _razvertka, x1, _b, _c, x2); } ); } } else { switch (_Type) { case Top: DownLeft = new PeanoCurve_2D(Right, _razvertka, _a, x1, _c, x2); TopLeft = new PeanoCurve_2D(Top, _razvertka, _a, x1, x2, _d); TopRight = new PeanoCurve_2D(Top, _razvertka, x1, _b, x2, _d); DownRight = new PeanoCurve_2D(Left, _razvertka, x1, _b, _c, x2); break; case Down: TopRight = new PeanoCurve_2D(Left, _razvertka, x1, _b, x2, _d); DownRight = new PeanoCurve_2D(Down, _razvertka, x1, _b, _c, x2); DownLeft = new PeanoCurve_2D(Down, _razvertka, _a, x1, _c, x2); TopLeft = new PeanoCurve_2D(Right, _razvertka, _a, x1, x2, _d); break; case Right: DownLeft = new PeanoCurve_2D(Top, _razvertka, _a, x1, _c, x2); DownRight = new PeanoCurve_2D(Right, _razvertka, x1, _b, _c, x2); TopRight = new PeanoCurve_2D(Right, _razvertka, x1, _b, x2, _d); TopLeft = new PeanoCurve_2D(Down, _razvertka, _a, x1, x2, _d); break; default: TopRight = new PeanoCurve_2D(Down, _razvertka, x1, _b, x2, _d); TopLeft = new PeanoCurve_2D(Left, _razvertka, _a, x1, x2, _d); DownLeft = new PeanoCurve_2D(Left, _razvertka, _a, x1, _c, x2); DownRight = new PeanoCurve_2D(Top, _razvertka, x1, _b, _c, x2); } } } } const __declspec(noalias) __forceinline PeanoCurve_2D* __restrict HitTest_2D(float x) const noexcept { int i = 0; const int _razvertka = this->razvertka; int num; const float this_a = this->a; x -= this_a; const float b_minus_a = this->b - this_a; const float inv_b_minus_a = 1.0f / b_minus_a; const PeanoCurve_2D* __restrict Curr = this; while (i != _razvertka) { const int shift = 1 << ++i + i; num = shift * x * inv_b_minus_a; x = fmaf(-ldexp(1.0f, -(i << 1)) * num, b_minus_a, x); const List currType = Curr->Type; switch (num) { case 0: Curr = (currType == Top || currType == Right) ? Curr->DownLeft : Curr->TopRight; break; case 1: Curr = (currType == Top || currType == Left) ? Curr->TopLeft : Curr->DownRight; break; case 2: Curr = (currType == Top || currType == Right) ? Curr->TopRight : Curr->DownLeft; break; default: Curr = (currType == Top || currType == Left) ? Curr->DownRight : Curr->TopLeft; } } return const_cast<PeanoCurve_2D * __restrict>(Curr); } const __declspec(noalias) __forceinline float FindX_2D(const float target_x1, const float target_x2) const noexcept { int _razvertka = this->razvertka; int _razvertka1 = _razvertka; float x1, x2, x = this->a; const float b_minus_a = this->b - x; const PeanoCurve_2D* __restrict Curr = this; while (_razvertka != 0) { const int exponent = _razvertka1 - _razvertka-- << 1; x1 = Curr->x1; x2 = Curr->x2; const List currType = Curr->Type; if (target_x1 > x1 && target_x2 > x2) { Curr = Curr->TopRight; if (currType == Top || currType == Right) { x = fmaf(ldexpf(1.0f, -(exponent + 1)), b_minus_a, x); } } else if (target_x1 < x1 && target_x2 > x2) { Curr = Curr->TopLeft; if (currType == Top || currType == Left) { x = fmaf(ldexpf(1.0f, -(exponent + 2)), b_minus_a, x); } else { x = fmaf(ldexpf(1.0f, -exponent) * 0.75f, b_minus_a, x); } } else if (target_x1 < x1 && target_x2 < x2) { Curr = Curr->DownLeft; if (currType == Down || currType == Left) { x = fmaf(ldexpf(1.0f, -(exponent + 1)), b_minus_a, x); } } else { Curr = Curr->DownRight; if (currType == Top || currType == Left) { x = fmaf(ldexpf(1.0f, -exponent) * 0.75f, b_minus_a, x); } else { x = fmaf(ldexpf(1.0f, -(exponent + 2)), b_minus_a, x); } } } return x; }

};

static std::vector<float, tbb::scalable_allocator<float>> Extr;
static std::vector<Interval*, tbb::scalable_allocator<Interval*>> R;
static boost::mpi::environment* __restrict g_env;
static boost::mpi::communicator* __restrict g_world;
static boost::optionalboost::mpi::status probe_status;
static std::aligned_storage_t<sizeof(PeanoCurve_2D), alignof(PeanoCurve_2D)> curveStorage;
static PeanoCurve_2D& __restrict Curve = reinterpret_cast<PeanoCurve_2D & __restrict>(curveStorage);

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline int AgpInit(const int peanoLevel, const float a, const float b, const float c, const float d) noexcept
{
peano_memory_manager.preallocate_for_depth(peanoLevel);
g_env = ::new boost::mpi::environment();
g_world = ::new boost::mpi::communicator();
int rank = g_world->rank();
if (rank) {
::new (&Curve) PeanoCurve_2D(List::Down, peanoLevel, a, b, c, d);
}
else {
::new (&Curve) PeanoCurve_2D(List::Top, peanoLevel, a, b, c, d);
}
return rank;
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpFinalize() noexcept
{
delete g_world;
delete g_env;
}

__declspec(align(16)) 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 const ar, const unsigned int) 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 float seed,
float** __restrict out_data, size_t* __restrict out_len) noexcept
{
const int rank = g_world->rank();
const int partner = rank ^ 1;
const float inv_divider = ldexpf(1.0f, -((Curve.razvertka << 1) + 1));
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;
float x_Rmax_1, x_Rmax_2;
float initial_length, dmax, threshold_03, inv_threshold_03;
float start_val, best_f, y_Rmax_1, y_Rmax_2;
float Mmax, m;
int schetchick = 0, mcQueenSpeed = 1;
float new_point, new_value;
const PeanoCurve_2D* __restrict pc;
float new_x1, new_x2;
Interval* __restrict promejutochny_otrezok;
Interval* __restrict curr;
Interval* __restrict curr1;
float currM, len1, len2, len_item;
size_t r_size;
float progress, alpha, betta, MULTIPLIER, global_coeff, GLOBAL_FACTOR;
Interval* __restrict top_ptr;
float interval_len;
int dummy;
const PeanoCurve_2D* __restrict p1L;
const PeanoCurve_2D* __restrict p2L;
CrossMsg outbound, inbound;
Interval* __restrict injected;
float cooling;
int T;
float k;

text
if (N == 1.0f) { initial_length = b - a; dmax = initial_length; threshold_03 = 0.3f * initial_length; inv_threshold_03 = 1.0f / threshold_03; start_val = ShekelFunc(a, seed); best_f = ShekelFunc(b, seed); x_Rmax_1 = a; x_Rmax_2 = b; y_Rmax_1 = start_val; y_Rmax_2 = best_f; Extr.reserve(static_cast<size_t>(global_iterations) << 2u); Extr.clear(); R.reserve(static_cast<size_t>(global_iterations) << 1u); R.clear(); R.emplace_back(new Interval(a, b, start_val, best_f, N)); std::push_heap(R.begin(), R.end(), ComparePtr); Mmax = R.front()->M; m = r * Mmax; while (true) { new_point = Shag(m, x_Rmax_1, x_Rmax_2, y_Rmax_1, y_Rmax_2, N, r); new_value = ShekelFunc(new_point, seed); 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); promejutochny_otrezok = R.back(); R.pop_back(); new_x1 = promejutochny_otrezok->x1; new_x2 = promejutochny_otrezok->x2; len2 = new_x2 - new_point; len1 = new_point - new_x1; interval_len = (len1 < len2) ? len1 : len2; if (interval_len < epsilon || ++schetchick == static_cast<int>(global_iterations)) { if (partner) { Extr.emplace_back(static_cast<float>(schetchick)); Extr.emplace_back(interval_len); *out_len = Extr.size(); *out_data = reinterpret_cast<float* __restrict>(CoTaskMemAlloc(sizeof(float) * (*out_len))); memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len)); } return; } curr = new Interval(new_x1, new_point, promejutochny_otrezok->y1, new_value, N); curr1 = new Interval(new_point, new_x2, new_value, promejutochny_otrezok->y2, N); currM = (curr->M > curr1->M) ? curr->M : curr1->M; r_size = R.size(); if ((len2 + len1) == dmax) { dmax = (len2 > len1) ? len2 : len1; size_t i = 0u;

#pragma loop(ivdep)
while (i < r_size) {
len_item = R[i]->x2 - R[i]->x1;
if (len_item > dmax) dmax = len_item;
++i;
}
}
if (mode) {
if ((threshold_03 > dmax && fmodf(static_cast<float>(schetchick), 3.0f) == 0.0f) || 10.0f * dmax < initial_length) {
if (currM > Mmax) { Mmax = currM; m = r * Mmax; }
progress = fmaf(-inv_threshold_03, dmax, 1.0f);
alpha = fmaf(progress, progress, 1.0f);
betta = 2.0f - alpha;
MULTIPLIER = (1.0f / dmax) * Mmax;
global_coeff = fmaf(MULTIPLIER, r, -MULTIPLIER);
GLOBAL_FACTOR = betta * global_coeff;
curr->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len1, curr->M * alpha));
curr1->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len2, curr1->M * alpha));
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) {
R[i]->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, R[i]->x2 - R[i]->x1, R[i]->M * alpha));
++i;
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
if (currM > Mmax) {
Mmax = currM; m = r * Mmax;
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) { R[i]->ChangeCharacteristic(m); ++i; }
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);
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) { R[i]->ChangeCharacteristic(m); ++i; }
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
}
}
R.emplace_back(curr);
std::push_heap(R.begin(), R.end(), ComparePtr);
R.emplace_back(curr1);
std::push_heap(R.begin(), R.end(), ComparePtr);
top_ptr = R.front();
x_Rmax_1 = top_ptr->x1;
x_Rmax_2 = top_ptr->x2;
y_Rmax_1 = top_ptr->y1;
y_Rmax_2 = top_ptr->y2;
}
}
else {
x_Rmax_1 = true_start;
x_Rmax_2 = true_end;
initial_length = true_end - true_start;
dmax = initial_length;
threshold_03 = 0.3f * initial_length;
inv_threshold_03 = 1.0f / threshold_03;
start_val = rank ? RastriginFunc(true_end, d - y_addition)
: RastriginFunc(true_start, c + y_addition);
y_Rmax_1 = start_val;
best_f = rank ? RastriginFunc(true_start, d - y_addition)
: RastriginFunc(true_end, c + y_addition);
y_Rmax_2 = best_f;
Extr.reserve(static_cast<size_t>(global_iterations) << 2u);
Extr.clear();
R.reserve(static_cast<size_t>(global_iterations) << 1u);
R.clear();
R.emplace_back(new Interval(true_start, true_end, start_val, best_f, N));
Mmax = R.front()->M;
m = r * Mmax;
while (true) {
probe_status = g_world->iprobe(partner, 2);
if (probe_status) {
if (partner) {
Extr.emplace_back(static_cast<float>(schetchick));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = reinterpret_cast<float __restrict>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
}
g_world->recv(partner, 2, dummy);
return;
}
cooling = ldexpf(1.0f, -(1.0f / 138.63f) * ++schetchick);
T = static_cast<int>(fmaf(20.0f, cooling, 10.0f));
k = fmaf(0.2f, cooling, 0.7f);
if (schetchick % (T >> 1) == 0) {
probe_status = g_world->iprobe(partner, 0);
if (probe_status) {
g_world->recv(partner, 0, mcQueenSpeed);
}
}
if (!(schetchick % T) == mcQueenSpeed) {
p1L = Curve.HitTest_2D(top_ptr->x1);
p2L = Curve.HitTest_2D(top_ptr->x2);
outbound = CrossMsg{ p1L->x1, p1L->x2, p2L->x1, p2L->x2, top_ptr->R };
if (mcQueenSpeed) {
g_world->send(partner, 0, 0);
}
else {
mcQueenSpeed = 2;
}
g_world->sendrecv(partner, 1, outbound, partner, 1, inbound);
injected = new Interval(Curve.FindX_2D(inbound.s_x1, inbound.s_x2), Curve.FindX_2D(inbound.e_x1, inbound.e_x2), RastriginFunc(inbound.s_x1, inbound.s_x2), RastriginFunc(inbound.e_x1, inbound.e_x2), N);
injected->R = inbound.Rtop * k;
R.emplace_back(injected);
std::push_heap(R.begin(), R.end(), ComparePtr);
}
new_point = Shag(m, x_Rmax_1, x_Rmax_2, y_Rmax_1, y_Rmax_2, N, r);
pc = Curve.HitTest_2D(new_point);
new_x1 = pc->x1;
new_x2 = pc->x2;
new_value = RastriginFunc(new_x1, new_x2);
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);
promejutochny_otrezok = R.back();
R.pop_back();
curr = new Interval(promejutochny_otrezok->x1, new_point, promejutochny_otrezok->y1, new_value, N);
curr1 = new Interval(new_point, promejutochny_otrezok->x2, new_value, promejutochny_otrezok->y2, N);
currM = (curr->M > curr1->M) ? curr->M : curr1->M;
len2 = promejutochny_otrezok->x2 - new_point;
len1 = new_point - promejutochny_otrezok->x1;
r_size = R.size();
if ((len2 + len1) == dmax) {
dmax = (len2 > len1) ? len2 : len1;
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) {
len_item = R[i]->x2 - R[i]->x1;
if (len_item > dmax) dmax = len_item;
++i;
}
}
if (mode) {
if ((threshold_03 > dmax && fmodf(static_cast<float>(schetchick), 3.0f) == 0.0f) || 10.0f * dmax < initial_length) {
if (currM > Mmax) { Mmax = currM; m = r * Mmax; }
progress = fmaf(-inv_threshold_03, dmax, 1.0f);
alpha = fmaf(progress, progress, 1.0f);
betta = 2.0f - alpha;
MULTIPLIER = (1.0f / dmax) * Mmax;
global_coeff = fmaf(MULTIPLIER, r, -MULTIPLIER);
GLOBAL_FACTOR = betta * global_coeff;
curr->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len1, curr->M * alpha));
curr1->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, len2, curr1->M * alpha));
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) {
R[i]->ChangeCharacteristic(fmaf(GLOBAL_FACTOR, R[i]->x2 - R[i]->x1, R[i]->M * alpha));
++i;
}
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
if (currM > Mmax) {
Mmax = currM; m = r * Mmax;
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) { R[i]->ChangeCharacteristic(m); ++i; }
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);
size_t i = 0u;
#pragma loop(ivdep)
while (i < r_size) { R[i]->ChangeCharacteristic(m); ++i; }
std::make_heap(R.begin(), R.end(), ComparePtr);
}
else {
curr->ChangeCharacteristic(m);
curr1->ChangeCharacteristic(m);
}
}
R.emplace_back(curr);
std::push_heap(R.begin(), R.end(), ComparePtr);
R.emplace_back(curr1);
std::push_heap(R.begin(), R.end(), ComparePtr);
top_ptr = R.front();
x_Rmax_1 = top_ptr->x1;
x_Rmax_2 = top_ptr->x2;
y_Rmax_1 = top_ptr->y1;
y_Rmax_2 = top_ptr->y2;
interval_len = x_Rmax_2 - x_Rmax_1;
if (interval_len < epsilon || schetchick == static_cast<int>(global_iterations)) {
if (partner) {
Extr.emplace_back(static_cast<float>(schetchick));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = reinterpret_cast<float __restrict>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
}
g_world->send(partner, 2, 0);
return;
}
}
}
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void Base_LNA_1_2_Mer_AGP_Free(float* __restrict p) noexcept
{
CoTaskMemFree(p);
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpStartWorkers() noexcept
{
g_world->send(1, 3, 1);
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpStopWorkers() noexcept
{
g_world->send(1, 3, 2);
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpWaitStartAndRun() noexcept
{
int command;
float* __restrict buf;
size_t len;
while (true) {
command = 0;
probe_status = g_world->iprobe(0, 3);
if (probe_status) {
g_world->recv(0, 3, command);
if (command == 1) {
Base_LNA_1_2_Mer_AGP(2.0f, 1000.0f, -2.2f, 1.8f, -2.2f, 1.8f,
2.5f, false, 0.001f, GetTickCount(),
&buf, &len);
}
else {
return;
}
}
}
} это мой код для поиска минимума, рассмотри подробно двумерную ветку в частности логику MPI пересылок, в частности логику кросс-инъекций, сейчас есть такая проблема что если бы я убрал это условие что принятие сообщения о кросс-инъекции происходит только по определённым итерациям счётчика и разрешил бы принимать сообщения о кросс-инъекциях сразу как они будут доступны то возникла бы такая проблема что оба процесса примерно одновременно приходили бы к участку отправки сообщения на завершения счёта что приводило к deadlock, это происходило потому что один процесс по счёту сильно опережал другой и когда у более быстрого процесса уже было много поисковой информации и он отправлял сообщение процессу у которого её меньше то медленный процесс мгновенно цеплялся за лучший интервал который уже близок к оптимальному и найден более быстрым процессом - и завешал счёт - это неплохо такое быстрое завершение счёта - проблема лишь в том что это приводило к дэдлоку и я был вынужден написать условие if (schetchick % (T >> 1) == 0) {, также в целом проанализируй код - как его ещё можно ускорить, не пиши про if (!(schetchick % T) == mcQueenSpeed) { это условие полностью корректно это моя разработка работающая похожим на мьютекс образом здесь, думай так долго как надо

Мысль

Analyzing MPI code for optimization

I’m analyzing the user's C++ MPI code involving cross-injections and deadlock potential. To avoid deadlock, I’ll suggest non-blocking MPI (like Isend/Irecv) or Boost.MPI's isend/irecv. A tagged protocol with a handshake might help for synchronization. Background threads may not be allowed, but they’ve already used iprobe, which should help.

Suggesting design improvements

To avoid deadlock, I suggest using an asymmetric send/recv order. For example, the rank with the lower ID initiates the send first, while the other does the receive first; alternatively, they can use MPI_Sendrecv — which they’re already using for cross-injections. Regarding termination, there’s an issue when both processes attempt to send and receive the termination tag simultaneously.

Examining potential deadlock causes

The main problem might be that both processes try to send tag 2 at the same time, leading to a blocking situation. They seem to return immediately after sending, without posting the receive, causing the other process’s iprobe to catch the incoming message. This could be where the deadlock happens, potentially due to the earlier tag 0 handshake.

Scanning code logic

The code sets cooling, T, and k on each iteration. When schetchick % (T >> 1) == 0, it probes tag 0 for mcQueenSpeed. If the condition !(schetchick % T) == mcQueenSpeed holds, they perform a cross-injection with sendrecv tag 1 to avoid deadlock. For tag 0, they either send 0 or set mcQueenSpeed to 2. The termination checks for partner’s tag 2 before outputting results.

Analyzing termination and memory management

It looks like the code might have a bug where the top_ptr is used before it’s initialized in the 2D branch. This could lead to issues if schetchick % T == 0 triggers, especially when T == 1 after many iterations. The code updates top_ptr later in the loop, so it's not a problem in most cases, but it still feels fragile. The termination handshake might cause deadlock when both processes are trying to send termination signals simultaneously. Switching to sendrecv could resolve this.

Solving deadlock and optimizing code

The code seems to encounter a potential deadlock when both processes try to send termination signals, waiting for acknowledgment that never comes. I suggest using sendrecv or isend to avoid this rendezvous deadlock. Also, for optimization, switching from static arrays in multi-threaded contexts can help prevent data races, and using MPI_ANY_TAG for flexible communication. Additionally, vectorizing trigonometric approximations seems like a good step for speed. Overall, simplifying the termination handshake would be key.

Optimizing message passing to avoid deadlock

To avoid deadlock, one solution could be for rank 0 to always send termination and rank 1 to only receive and exit, ensuring that only one side ever sends termination. Using sendrecv will help avoid simultaneous sends. Also, to avoid blocking, consider using a "drain mailbox" pattern, probing for all possible messages in a fixed order.

Addressing deadlock and optimizations

To avoid deadlock, use sendrecv or a symmetric handshake with rank-ordering, or implement non-blocking operations. Ensure variables like top_ptr are initialized correctly before use. For performance, precompute values where possible, tweak heap operations, and fine-tune memory allocations. Additionally, consider mailbox-draining to handle cross-injection consistently.

Thought for 3m 4s

Ответ

ниже — разбор именно 2-мерной ветки, схемы MPI-обмена и «кросс-инъекций», плюс конкретные правки, которые устраняют дедлок без «дросселирования» приёма, и идеи по ускорению. я не трогаю ваше условие if (!(schetchick % T) == mcQueenSpeed) — вы сами сказали его не обсуждать.

что сейчас делает 2D-ветка (вкратце)

  • цикл итераций поддерживает кучу R по характеристике интервалов.
  • периодически выполняется «кросс-инъекция» через sendrecv с тегом 1 (симметричный обмен CrossMsg).
  • есть «сигнальные» послания с тегом 0 (управление скоростью).
  • завершение — послание с тегом 2: в начале каждой итерации делается iprobe(partner, 2) → при наличии — recv(...,2,...) и возврат; при локальном завершении — простая send(partner, 2, 0) и немедленный return.

откуда берётся дедлок при «всегда-включённом» приёме кросс-инъекций

как только вы снимаете ограничение на приём инъекций, медленный процесс получает «почти оптимальный» интервал от быстрого и тоже очень быстро достигает условия останова. оба процесса почти одновременно заходят в ветку «я завершаю», где выполняют:

cpp
g_world->send(partner, 2, 0); return;

boost::mpi::send — блокирующий вызов. Если оба процесса одновременно делают send на один и тот же тег/направление, а соответствующий recv не был заранее «выставлен», легко попадается классический rendezvous-дедлок (особенно если MPI выбрал протокол без буферизации для данного сообщения). У вас нет симметричного recv после send, а iprobe+recv стоит только в начале следующей итерации, до которой вы уже не доходите — вы сразу выходите из функции. Отсюда и взаимная блокировка.

минимальная правка, гарантирующая отсутствие дедлока при завершении

сделайте завершение таким же симметричным, как кросс-инъекция: используйте sendrecv (или строго упорядоченную пару recv→send / send→recv по рангу).

Вариант A (drop-in): sendrecv на теге 2

cpp
// ... перед return в обоих местах завершения 2D-ветки: int dummy = 0; g_world->sendrecv(partner, 2, dummy, partner, 2, dummy); return;

MPI_Sendrecv специально придумана, чтобы такие «обоюдные» обмены были безопасными независимо от того, кто первый дошёл.

Вариант B (ранг-упорядоченный рукопожатный финал)

cpp
int dummy = 0; if (g_world->rank() == 0) { g_world->send(partner, 2, dummy); g_world->recv(partner, 2, dummy); } else { g_world->recv(partner, 2, dummy); g_world->send(partner, 2, dummy); } return;

Этот вариант тоже исключает дедлок и не требует sendrecv, если хочется остаться на send/recv.

Вариант C (неблокирующее завершение)

Если принципиально хочется оставить единичную посылку, используйте неблокирующую отправку и дождитесь её локально:

cpp
int dummy = 0; auto req = g_world->isend(partner, 2, dummy); while (!req.test()) { // опционально: "сливайте почту" while (g_world->iprobe(partner, 1)) { CrossMsg in; g_world->recv(partner, 1, in); /* игнорируем поздние инъекции */ } while (g_world->iprobe(partner, 0)) { int tmp; g_world->recv(partner, 0, tmp); } } // (никаких recv на 2 не нужно) return;

Но проще и чище — Вариант A.

Итог: после замены send(partner, 2, ...) на sendrecv/упорядоченную пару вы можете убрать «предохранитель» с редким опросом и принимать кросс-инъекции немедленно — дедлок при одновременном завершении не случится.

«Почтовый ящик»: порядок обработки сообщений в итерации

Чтобы система оставалась отзывчивой и без «затыков», рекомендую в начале каждой итерации полностью вычерпывать входящие сообщения по приоритету:

  1. tag=2 (terminate) — немедленно принять и выйти;
  2. tag=1 (инъекции) — принять все доступные (в цикле while (iprobe(...,1))), применить;
  3. tag=0 (скорость/сигнал) — принять все доступные.

Это снимает накопление «хвостов» и позволяет смело включить «принимать инъекции сразу».

Пример каркаса (псевдокод внутри вашей 2D-ветки перед основной логикой итерации):

cpp
// 1) terminate if (g_world->iprobe(partner, 2)) { int dummy; if (partner) { /* сформировать Extr и выдать */ } g_world->recv(partner, 2, dummy); return; } // 2) injections while (g_world->iprobe(partner, 1)) { CrossMsg inbound; CrossMsg outbound = /* соберите локальный лучший top_ptr в msg (см. инициализацию top_ptr ниже) */; g_world->sendrecv(partner, 1, outbound, partner, 1, inbound); /* примените inbound */ } // 3) signals while (g_world->iprobe(partner, 0)) { g_world->recv(partner, 0, mcQueenSpeed); }

(Логику с mcQueenSpeed/T вы оставляете; показан только каркас приоритезации.)

ещё важные точечные правки/надёжность

  1. Инициализация top_ptr до первого использования.
    Сейчас в 2D-ветке top_ptr впервые используется в блоке кросс-инъекции до первого присваивания в конце итерации. Обычно на первой итерации условие не срабатывает, но это хрупко. Сразу после первичной инициализации кучи сделайте:
cpp
std::make_heap(R.begin(), R.end(), ComparePtr); Interval* __restrict top_ptr = R.front(); // <-- безопасно для первого outbound
  1. Плавающее равенство:
if ((len2 + len1) == dmax) { ... пересчёт dmax сканом ... }

Сравнение == для float ненадёжно. Лучше

cpp
if (len1 + len2 >= dmax - 1e-12f) { ... }

или вообще избавиться от N-скана (см. ускорения ниже).

  1. Мир из двух процессов.
    Код предполагает size == 2 (партнёр rank ^ 1). Защитите это инвариантом в AgpInit:
cpp
if (g_world->size() != 2) { /* либо throw, либо assert */ }
  1. Безопасность индекса в calculate_tree_size_bytes.
    Массив на 7 элементов и индекс max_depth - 7u. Добавьте assert на допустимый диапазон.

  2. Побочные эффекты static-массивов в FABE13_SINCOS.
    Они «разделяемые» между потоками. В 2D-ветке вы их не вызываете, но если потом добавится многопоточный вызов — будет data race. На будущее: сделать буферы автоматическими/передаваемыми.

  3. Неинициализированная interval_len при «раннем» выходе по сообщению.
    В начале цикла, при получении тега 2, вы записываете interval_len в выход кортеж только если partner. Если хотите гарантированно валидное значение, обновляйте interval_len сразу после top_ptr = R.front().

ускорение: что даст «много» за малую цену

(A) Уберите O(n)-скан кучи под пересчёт dmax.
Сейчас вы часто делаете:

cpp
if ((len2 + len1) == dmax) { dmax = max(len1, len2); for (i in R) dmax = max(dmax, R[i]->x2 - R[i]->x1); }

Это линейно по |R|. Вместо этого поддерживайте вторую кучу (или просто max-переменную) по длинам:

  • при каждом split у вас появляются len1 и len2 — обновляйте dmax = max(dmax, len1, len2);
  • когда извлекаете вершину, не сканируйте всю R. Если боитесь, что «старый» dmax завышен, допускайте инертность: пересчитывайте истинный dmax (полный скан) только когда от него реально зависит ветка с «глобальными коэффициентами» (т.е. редко).

На практике это снимает самый частый O(n)-хвост.

(B) «Ленивая» глобальная перенастройка характеристик.
Вы часто проходите по всей R и зовёте ChangeCharacteristic для смены m/глобальных коэффициентов. Это дорогой O(n) проход + последующий make_heap. Два приёма, чтобы реже это делать:

  • Введите epoch и «глобальные» параметры (например, global_alpha, global_factor, m) в структуру, которую видит компаратор. Пока новая конфигурация монотонно меняет ключи всем интервалам, относительный порядок обычно сохраняется. Тогда вы обновляете параметры без прохода по R и без make_heap.
  • Если всё же порядок «поплыл», делайте make_heap не сразу, а по триггеру (например, раз в K итераций или при превышении порога изменения параметра). Это сильно уменьшает число перестроек.

Да, в худшем случае порядок может измениться и вы на время получите «субоптимальный» top, но на практике это окупается снижением накладных расходов.

(C) Память под Interval.
У вас уже есть предвыделитель — отлично. Дайте вектору R больший reserve (вы делаете, хорошо) и используйте «плотное» хранение полей в Interval (они уже упакованы). Если захотите ещё выжать кеш:

  • вынести часто используемое подмножество полей (x1, x2, y1, y2, M, R) в SoA буферы и считать ChangeCharacteristic батчами по 8 с AVX2 (у вас уже много FMA).

(D) Меньше веток в «косинусах».
Для 2D-ветки вы используете Rastrigin и уже оптимизированные аппроксимации. Если будет горячая точка — можно свернуть редукцию по восьмёркам в FABE13_SINCOS (но сейчас это не узкое место у 2D-ветки).

мелкие, но полезные шлифовки

  • В HitTest_2D и FindX_2D добавьте скобки для сдвигов, чтобы не зависеть от приоритетов:
    cpp
    const int shift = 1 << ( (++i) + i ); // было 1 << ++i + i const int exponent = ( (_razvertka1 - (_razvertka--)) << 1 );
  • В Shag вы лезете в биты diff через *(const int*)&diff — это UB по strict aliasing. На MSVC прокатывает, но корректнее через memcpy или _mm_movemask_ps-трюк, либо std::bit_cast<int>(diff) (C++20).

про саму кросс-инъекцию

  • Вы грамотно используете sendrecv (тег 1) — это уже «правильный» способ симметричного обмена.
  • Чтобы сделать приём полностью немедленным и безопасным:
    • держите «почтовый ящик» (см. выше) и принимайте все доступные инъекции перед вычислением шага;
    • лимитируйте применение: если инъекций пришло >K подряд, применяйте только лучшую/две лучшие по R за итерацию. Это даёт эффект «демпфера» без дополнительного протокола.
    • «скоростной» тег 0 оставьте как есть — он дешёвый. Но теперь он не нужен для предотвращения дедлока — только как сигнал адаптации.

если коротко: причина дедлока — симметричный send на завершение без встречного recv. Лечится переводом финала на sendrecv (или ранжированную пару recv/send). После этого можно принимать кросс-инъекции сразу, без защитных «окон». Плюс несколько недорогих правок (инициализация top_ptr, отказ от == для float, удаление O(n)-сканов, «ленивая» перенастройка характеристик) дают заметное ускорение без изменения математической сути алгоритма.

Поделиться этим Q&A