Коли обчислення з обмеженою пропускною здатністю пам’яті виконуються в середовищах спільної пам’яті (наприклад, з потоком через OpenMP, Pthreads або TBB), існує дилема, як забезпечити правильну розподіл пам’яті по фізичній пам’яті, таким чином, щоб кожен потік в основному отримував доступ до пам’яті "локальна" шина пам'яті. Хоча інтерфейси не є портативними, у більшості операційних систем є способи встановлення спорідненості потоків (наприклад, pthread_setaffinity_np()
для багатьох систем POSIX, sched_setaffinity()
для Linux, SetThreadAffinityMask()
для Windows). Існують також такі бібліотеки, як hwloc для визначення ієрархії пам'яті, але, на жаль, більшість операційних систем ще не надають способи встановлення політики пам'яті NUMA. Linux є помітним винятком з libnumaдозволяючи додатку маніпулювати політикою пам'яті та міграцією сторінок при деталізації сторінки (в основному з 2004 року, таким чином, широко доступний). Інші операційні системи очікують, що користувачі дотримуються неявної політики "першого дотику".
Робота з політикою "першого дотику" означає, що абонент повинен створювати та поширювати потоки з будь-якою спорідненістю, яку вони планують використовувати пізніше під час першого запису до щойно виділеної пам'яті. (Дуже небагато систем налаштовано таким чином, що malloc()
насправді знаходить сторінки. Він просто обіцяє їх знайти, коли вони фактично винні, можливо, різними потоками.) Це означає, що виділення з використанням calloc()
або негайної ініціалізації пам'яті після розподілу з використанням memset()
шкідливо, оскільки воно буде схильне до помилок вся пам'ять на шині пам'яті в ядрі, що працює з розподільним потоком, що призводить до найгіршого пропускної здатності пам'яті, коли доступ до пам'яті здійснюється з декількох потоків. Те саме стосується new
оператора C ++, який наполягає на ініціалізації багатьох нових розподілів (наприклад,std::complex
). Деякі спостереження щодо цього середовища:
- Виділення можна зробити «колективними нитками», але тепер розподіл стає змішаним у моделі нарізки, що небажано для бібліотек, яким, можливо, доведеться взаємодіяти з клієнтами, використовуючи різні моделі різьблення (можливо, кожна з власними пулами потоків).
- RAII вважається важливою частиною ідіоматичного C ++, але він, здається, є активно шкідливим для роботи пам'яті в середовищі NUMA. Розміщення
new
може використовуватися з пам'яттю, виділеною черезmalloc()
або підпрограмиlibnuma
, але це змінює процес розподілу (що, на мою думку, є необхідним). - EDIT: Моя раніше заява про оператора
new
була неправильною, вона може підтримувати кілька аргументів, див. Відповідь Четана. Я вважаю, що все ще існує занепокоєння щодо використання бібліотеками або контейнерами STL для використання зазначеної спорідненості. Може бути запаковано кілька полів, і це може бути незручно для того, щоб, наприклад,std::vector
перерозподіляти з активним менеджером контексту активний перенос. - Кожен потік може виділяти та помиляти власну приватну пам'ять, але тоді індексація в сусідні регіони є складнішою. (Розглянемо розріджений векторний матричний добуток із розділом рядків матриці та векторами; для індексації невідомій частині x потрібна складніша структура даних, коли x не є суміжним у віртуальній пам'яті.)
Чи вважаються якісь рішення розподілу / ініціалізації NUMA ідіоматичними? Чи залишив я інші критичні проблеми?
(Я не маю в виду для мого C приклади ++ має на увазі акцент на тій мові, однак C ++ мову кодує деякі рішення про управління пам'яттю , що мова , як C НЕ відбувається , таким чином , існує тенденція до більш опору , коли припускаючи , що C ++ програмісти роблять ті , речі інакше.)