Сучасний підхід до створення std :: вектор виділяє вирівняну пам'ять


11

Наступне питання пов'язане, однак відповіді на старі, і коментар від користувача Marc Glisse передбачає , що є нові підходи , починаючи з C ++ 17 до вирішення цієї проблеми , які не можуть бути адекватно обговорені.

Я намагаюся вирівняти пам'ять, що працює належним чином для SIMD, але все ще маю доступ до всіх даних.

Якщо в Інтернеті я створюю поплавковий вектор типу __m256і зменшую розмір в 8 разів, це дає мені вирівняну пам'ять.

Напр std::vector<__m256> mvec_a((N*M)/8);

Трохи хакітним способом я можу наводити покажчики на векторні елементи, щоб плавати, що дозволяє мені отримувати доступ до окремих значень плавучого.

Натомість я хотів би мати std::vector<float>правильне вирівнювання, і таким чином можна завантажувати в __m256інші SIMD типи без сегментації.

Я дивився в align_alloc .

Це може дати мені масив у стилі C, який правильно вирівняний:

auto align_sz = static_cast<std::size_t> (32);
float* marr_a = (float*)aligned_alloc(align_sz, N*M*sizeof(float));

Однак я не знаю, як це зробити std::vector<float>. Надання std::vector<float>права власності на marr_a , здається, неможливо .

Я бачив кілька пропозицій про те, що мені слід написати спеціальний розподільник , але це, здається, багато роботи, і, можливо, з сучасним C ++ є кращий спосіб?


1
без segfaulting ... або без потенційного уповільнення розбиття кеш-рядків при використанні _mm256_loadu_ps(&vec[i]). (Не дивлячись на те, зверніть увагу , що з параметрами налаштування по замовчуванню, GCC розщеплюється гарантованою вирівняний 256-бітові навантажень / магазинів в vmovups XMM / vinsertf128. Так що це перевага для використання _mm256_loadбільш , loaduякщо ви дбаєте про те , як ваш код компілюється на GCC якщо хто - то забуває використання -mtune=...або -march=варіанти.)
Пітер Кордес,

Відповіді:


1

Усі контейнери в стандартній бібліотеці C ++, включаючи вектори, мають необов'язковий параметр шаблону, який визначає розподільник контейнера , і реально реалізувати свій власний не дуже багато роботи:

class my_awesome_allocator {
};

std::vector<float, my_awesome_allocator> awesomely_allocated_vector;

Вам доведеться написати трохи коду, який реалізує ваш розподільник, але це буде не набагато більше коду, ніж ви вже написали. Якщо не потрібно попередньо C ++ 17 підтримки вам потрібно тільки реалізувати виділити () і DEALLOCATE () методи, це все .


Вони також повинні спеціалізуватисяallocator_traits
NathanOliver

1
Це може бути хорошим місцем для канонічної відповіді з прикладом того, що люди можуть скопіювати / вставити, щоб перестрибнути через набридливі обручі C ++. (Бонусні бали, якщо є спосіб дозволити std :: vector спробувати перерозподілити місце замість звичайного мозкового коду C ++ завжди призначати + копію.) Звичайно, зауважте, що це vector<float, MAA>не сумісно з типом vector<float>(і не може бути тому, що все, що робиться .push_backна звичайній, std::vector<float>складеній без цього розподільника, може зробити нове виділення та скопіювати в мінімально вирівняну пам’ять. І нове / видалення не сумісне з align_alloc / free)
Пітер Кордес,

1
Я не думаю, що є гарантія того, що вказівник, повернутий з алокатора, безпосередньо використовується як базовий адресу std::vectorмасиву 's. Наприклад, я міг уявити собі реалізацію std::vectorвикористання лише одного вказівника на виділену пам'ять, який зберігає кінець / ємність / розподільник у пам'яті до діапазону значень. Це легко може зірвати вирівнювання, зроблене алокатором.
Дітмар Кюль

1
За винятком того, що std::vectorце гарантує. Ось для чого це використовується. Можливо, вам слід переглянути, що тут визначено стандартом C ++.
Сам Варшавчик

1
> Вони також повинні спеціалізуватися allocator_traits- Ні, вони ні. Все, що потрібно - це реалізувати сумісний розподільник.
Андрій Семашев
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.