std::vector<Interval> R; R.reserve(global_iter...
बनाया गया: 21 सितंबर 2025
बनाया गया: 21 सितंबर 2025
textstd::vector<Interval> R; R.reserve(global_iterations * 8u); R.emplace_back(std::pair{ true_start, end_val }, std::pair{ true_end, best_f }, N); - такие строчки есть в моей экспортируемой функции из DLL, она экспортируется в управляемый с++/clr код winforms netframework 8.1.2 - ошибка возникает именно при занесении в вектор 100 процентов в этой строчке R.emplace_back(std::pair{ true_start, end_val }, std::pair{ true_end, best_f }, N); - когда проверял без неё - всё работало, конструктор: __declspec(align(64u)) struct Interval final {
public:
const std::pair<float, float> start;
const std::pair<float, float> end;
const float delta_x;
const float delta_y;
const float N_factor;
const float M;
float R;
text__declspec(noalias) __forceinline Interval(const std::pair<float, float> _start, const std::pair<float, float> _end, const uint64_t _N) noexcept : start(_start) , end(_end) , delta_x(_end.first - _start.first) , delta_y(_end.second - _start.second) , N_factor(_N == 1u ? delta_x : _N == 2u ? sqrtf(delta_x) : powf(delta_x, 1.0f / _N)) , M(fabsf(delta_y)* (1.0f / N_factor)) { } __declspec(noalias) __forceinline void ChangeCharacteristic(const float _m) noexcept { R = fmaf( -(end.second + start.second), 2.0f, fmaf(_m, N_factor, (delta_y * delta_y) * (1.0f / (_m * N_factor)))); }
}; - основное вызывающее приложение использует mpi и запускается через mpiexec на двух процессах, и mpi пересылки работают нормально, но именно при занесении в вектор возникают исключения, причём если заносить в вектор примитивные типы то проблем не возникает, я пробовал изолированно создавать интервалы - там тоже нет ошибки, интервалы создаются, так что ошибка вряд ли в конструкторе, std::vector<float> Extr;
Extr.reserve(global_iterations * 8u); Extr.emplace_back(1.0f); - это отрабатывает без проблем, найди проблему
Проблема почти наверняка в перевыравнивании (overallocation) для Interval
: вы задали __declspec(align(64))
, а std::vector
в вашем окружении (MSVC + /clr + .NET WinForms) не гарантирует корректную память с такой «сверх-» выравненностью без спец. поддержки aligned new или кастомного аллокатора. В результате при emplace_back(...)
попадаем в память, выровненную лишь до max_align_t
(обычно 16), а не до 64 — и получаем краш именно на вставке.
Почему это похоже именно на выравнивание:
float
) всё ок — они не требуют 64-байтного выравнивания.Interval
отдельно работает — там память выделяете «как обычно», без контейнера.const
-поля тут не мешают vector
(ему нужен move-конструктор, а он будет сгенерирован); но падение на первом emplace_back
при уже сделанном reserve
указывает именно на проблемы адреса/выравнивания, а не на перемещения/копии.Выберите один из безопасных вариантов (от самого простого к чуть более «инженерному»):
cpp// было: // __declspec(align(64)) struct Interval final { ... }; // станет: struct Interval final { ... };
Чаще всего выигрыша от 64-байтного align здесь нет, а стабильность важнее. Это самый быстрый фикс.
alignas(16)
:cppalignas(16) struct Interval final { ... };
Обычно 16 поддерживается без проблем даже под /clr.
_aligned_malloc/_aligned_free
:cpptemplate<class T, std::size_t Align> struct aligned_allocator { using value_type = T; aligned_allocator() noexcept {} template<class U> aligned_allocator(const aligned_allocator<U, Align>&) noexcept {} T* allocate(std::size_t n) { void* p = _aligned_malloc(n * sizeof(T), Align); if (!p) throw std::bad_alloc(); return static_cast<T*>(p); } void deallocate(T* p, std::size_t) noexcept { _aligned_free(p); } }; using IntervalVec = std::vector<Interval, aligned_allocator<Interval, 64>>; IntervalVec R; R.reserve(global_iterations * 8u); R.emplace_back(std::pair{true_start, end_val}, std::pair{true_end, best_f}, N);
new
/delete
у Interval
на выровненный:cppvoid* operator new(std::size_t sz) { if (void* p = _aligned_malloc(sz, 64)) return p; throw std::bad_alloc(); } void operator delete(void* p) noexcept { _aligned_free(p); }
Но при размещении в std::vector
для массивов может использоваться array new у аллокатора, так что вариант с кастомным аллокатором контейнера (п.3) надёжнее.
alignas(64)
без аллокатора — убедитесь, что проект собирается минимум с C++17 и включён флаг MSVC /Zc:alignedNew
. Под /clr
это иногда не работает/работает не во всех версиях MSVC, поэтому я бы не рассчитывал только на этот флаг в смешанном проекте.const
делают тип не присваиваемым (no move-assign), но для std::vector
это не проблема — при перераспределении он использует move-конструктор/копию, а не присваивание. С этим всё ок.R
(поле в структуре) у вас не инициализируется в конструкторе — это не причина падения, но имейте в виду, что до первого вызова ChangeCharacteristic
значение мусорное.Краш вызван несоответствием выравнивания элементов Interval
и реального выравнивания памяти, которую выдаёт std::vector
в вашем окружении (/clr). Самый быстрый фикс — убрать __declspec(align(64))
или снизить до 16. Если 64 действительно нужно для кеш-линий/false sharing — используйте кастомный выровненный аллокатор (п.3).