Мені цікаво, наскільки все ще діє Ульріха Дреппера, що повинен знати кожен програміст про пам'ять з 2007 року. Також я не зміг знайти більш нову версію, ніж 1.0 або errata.
Мені цікаво, наскільки все ще діє Ульріха Дреппера, що повинен знати кожен програміст про пам'ять з 2007 року. Також я не зміг знайти більш нову версію, ніж 1.0 або errata.
Відповіді:
Наскільки я пам’ятаю, вміст Drepper описує основні поняття щодо пам’яті: як працює кеш процесора, що таке фізична та віртуальна пам’ять та як ядро Linux обробляє цей зоопарк. Можливо, у деяких прикладах є застарілі посилання API, але це не має значення; це не вплине на актуальність основних понять.
Отже, будь-яку книгу чи статтю, яка описує щось основне, не можна назвати застарілим. "Те, що кожен програміст повинен знати про пам'ять", безумовно, варто прочитати, але, я думаю, це не для "кожного програміста". Він більше підходить для системних / вбудованих / ядер.
Посібник у форматі PDF знаходиться за посиланням https://www.akkadia.org/drepper/cpumemory.pdf .
Він все ще відмінний і дуже рекомендується (як я вважаю, і я думаю, що інші фахівці з налаштування продуктивності). Було б здорово, якби Ульріх (або хто-небудь інший) написав оновлення 2017 року, але це було б багато роботи (наприклад, повторний запуск орієнтирів). Дивіться також інші налаштування продуктивності x86 та посилання на оптимізацію SSE / asm (та C / C ++) у програміx86 тег вікі . (Стаття Ульріха не відрізняється від x86, але більшість (усіх) його орієнтирів розміщені на апаратному забезпеченні x86.)
Деталі апаратного забезпечення низького рівня про те, як працюють DRAM та кеші, все ще застосовуються . DDR4 використовує ті самі команди, що й описані для DDR1 / DDR2 (пакет читання / запис). Удосконалення DDR3 / 4 не є принциповими змінами. AFAIK, усі незалежні від арки речі все ще застосовуються, наприклад, до AArch64 / ARM32.
Дивіться також розділ " Затримка зв'язаних платформ" у цій відповіді, щоб отримати важливі деталі про вплив затримки пам'яті / L3 на однопоточну пропускну здатність: bandwidth <= max_concurrency / latency
і це фактично основне вузьке місце для однопотокової смуги пропускання на сучасному багатоядерному процесорі, як Xeon . Але чотирьохядерний робочий стіл Skylake може наблизитися до збільшення пропускної здатності DRAM за допомогою однієї нитки. Це посилання містить дуже гарну інформацію про магазини NT порівняно з звичайними магазинами на x86. Чому Skylake настільки кращий, ніж Broadwell-E, для однопотокової пропускної здатності пам'яті? є підсумком.
Таким чином, пропозиція Ульріха в 6.5.8 Використання всієї пропускної здатності щодо використання віддаленої пам’яті для інших вузлів NUMA, а також власного, є контрпродуктивною для сучасного обладнання, де контролери пам'яті мають більшу пропускну здатність, ніж може використовувати одне ядро. Можливо, ви можете уявити ситуацію, коли є користь для запуску декількох потоків, що відчувають пам’ять, на одному і тому ж вузлі NUMA для міжпотокового зв'язку з низькою затримкою, але змушення їх використовувати віддалену пам'ять для речей, що не залежать від затримки з високою пропускною здатністю. Але це досить малозрозуміло, як правило, просто розділяйте нитки між NUMA-вузлами і змушуйте їх використовувати локальну пам'ять. Пропускна здатність на основі ядра чутлива до затримки через обмеження максимальної конкурентоспроможності (див. Нижче), але всі ядра в одному сокеті зазвичай можуть перевищувати насичення контролерів пам'яті в цьому сокеті.
Одне головне, що змінилося, це те, що попередній вибір обладнання набагато кращий, ніж на Pentium 4, і він може розпізнати жорсткі шаблони доступу до досить великого кроку та декількох потоків одразу (наприклад, один вперед / назад на 4 к сторінки). Інструкція з оптимізації Intel описує деякі деталі попередньо встановлених HW в різних рівнях кешу для їх мікроархітектури сімейства Sandybridge. Ivybridge та пізніші версії мають апаратний попередній вибір, замість того, щоб чекати пропуску кешу на новій сторінці, щоб викликати швидкий старт. Я припускаю, що в посібнику з оптимізації AMD має подібні матеріали. Остерігайтеся, що посібник від Intel також сповнений старих порад, деякі з яких корисні лише для P4. Розділи, що стосуються Сендібріджу, звичайно точні для SnB, але, наприкладрозшарування мікроплавких вуоп змінилося в HSW, і керівництво не згадує про це .
Звичайна порада в цей час - видалити весь попередній вибір SW зі старого коду , і лише розглянути можливість його повернути, якщо профілювання показує пропуски кешу (і ви не насичуєте пропускну здатність пам'яті). Попередня вибір обох сторін наступного кроку двійкового пошуку все ще може допомогти. наприклад, як тільки ви вирішите, який елемент подивитись далі, попередньо встановіть елементи 1/4 та 3/4, щоб вони могли завантажуватися паралельно з серединою завантаження / перевірки.
Пропозиція використовувати окремий потік попереднього вибору (6.3.4) є абсолютно застарілим , я думаю, і він був хороший лише для Pentium 4. У P4 було гіперточення (2 логічних ядра, що розділяють одне фізичне ядро), але недостатньо кешу слідів (і / або ресурси, що виконуються поза замовленням) для отримання пропускної спроможності за допомогою двох повних обчислювальних потоків на одному ядрі. Але сучасні процесори (Сендібридж-сімейство та Ryzen) набагато бідніші, і вони повинні або запускати справжню нитку, або не використовувати гіпертокування (залиште інше логічне ядро в режимі очікування, щоб соло-потік мав усі ресурси замість того, щоб розділити ROB).
Підготовка програмного забезпечення завжди була "крихкою" : правильна магічна настройка номерів для отримання прискорення залежить від деталей обладнання та, можливо, завантаження системи. Занадто рано, і його виселяють перед завантаженням попиту. Надто пізно, і це не допомагає. Ця стаття в блозі містить код + графіки для цікавого експерименту із використанням попереднього вибору SW на Haswell для попереднього вибору непослідовної частини проблеми. Див. Також Як правильно користуватися інструкціями щодо попереднього вибору? . Попереднє вилучення NT цікаве, але ще більш крихке, оскільки раннє виселення з L1 означає, що вам доведеться пройти весь шлях до L3 або DRAM, а не тільки до L2. Якщо вам потрібен кожен останній спад продуктивності, і ви можете налаштуватися на конкретну машину, SW попередній вибір варто переглянути для послідовного доступу, але цеМожливо, це все ще буде сповільненням, якщо вам доведеться виконати достатню кількість АЛУ роботи, під час наближення до вузького місця в пам'яті.
Розмір кеш-лінії все ще становить 64 байти. (Пропускна здатність L1D для читання / запису дуже велика. Сучасні процесори можуть робити 2 векторні навантаження на годинник + 1 векторний магазин, якщо все це потрапляє в L1D. Див. Як кеш може бути таким швидким? ). З AVX512, розмір рядка = ширина вектора, тож ви можете завантажити / зберегти весь рядок кешу в одній інструкції. Таким чином, кожен нерівне завантаження / зберігання перетинає межу кеш-лінії замість кожного іншого для 256b AVX1 / AVX2, що часто не сповільнює циклічне перетворення на масив, який не був у L1D.
Інструкції щодо нерівного завантаження мають нульовий штраф, якщо адреса вирівнюється під час виконання, але компілятори (особливо gcc) роблять кращий код під час автоматичної перевірки, якщо вони знають про будь-які гарантії вирівнювання. Насправді нестандартні операційні операції, як правило, швидкі, але розбиття сторінок все ж болить (набагато менше на Skylake, хоча; лише 11 зайвих циклів затримки проти 100, але все-таки пропускний показник).
Як передбачив Ульріх, в наші дні кожна мультирозетна система є NUMA: інтегровані контролери пам'яті є стандартними, тобто немає зовнішнього Northbridge. Але SMP більше не означає мульти сокет, оскільки багатоядерні процесори широко поширені. Процесорні процесори Intel від Nehalem до Skylake використовували великий включений кеш-пам'ять L3 як резервний стоп для когерентності між ядрами. Процесорні процесори AMD різні, але в деталях мені не так зрозуміло.
У Skylake-X (AVX512) більше немає включеного L3, але я думаю, що досі існує тег-каталог, який дозволяє перевіряти, що є в кеші де-небудь на чіпі (і якщо так, де), не фактично транслюючи снопок на всі ядра. На жаль, SKX використовує сітку, а не кільцеву шину , із загалом ще гіршими затримками, ніж попередні багатоядерні Xeons, на жаль.
По суті, всі поради щодо оптимізації розміщення пам'яті все ще застосовуються, лише деталі того, що відбувається, коли ви не можете уникнути пропусків кеш-пам'яті або суперечок, відрізняються.
6.4.2 Атомні операційні можливості : тест, що показує цикл повторної спроби CAS, як в 4 рази гірший, ніж арбітражний арбітраж lock add
, ймовірно, все ще відображає випадок максимальної суперечки . Але в реальних багатопотокових програмах синхронізація зводиться до мінімуму (тому що це дорого), тому суперечка низька, і цикл CAS-повторної спроби, як правило, досягає успіху без необхідності повторювати.
C ++ 11 std::atomic
fetch_add
буде компілюватися до lock add
(або lock xadd
якщо використовується повернене значення), але алгоритм, що використовує CAS, щоб зробити те, що неможливо зробити за допомогою lock
інструкції ed, зазвичай не є катастрофою. Використовуйте C ++ 11std::atomic
або C11 stdatomic
замість застарілих __sync
вбудованих програм GCC або новіших __atomic
вбудованих програм, якщо ви не хочете змішувати атомний та неатомний доступ до одного місця ...
8.1 DWCAS ( cmpxchg16b
) : Ви можете примусити gcc випромінювати його, але якщо вам потрібні ефективні навантаження лише однієї половини об'єкта, вам потрібні некрасиві union
хаки: Як я можу реалізувати лічильник ABA з c ++ 11 CAS? . (Не плутайте DWCAS з DCAS з двох окремих місць пам’яті . Безблокова атомна емуляція DCAS неможлива з DWCAS, але транзакційна пам'ять (як x86 TSX) робить це можливим.)
8.2.4 транзакційна пам'ять : Після пари помилкових запусків (випущених та відключених оновленням мікрокоду через рідко викликану помилку), Intel має працюючу транзакційну пам’ять у пізній моделі Broadwell та всіх процесорах Skylake. Дизайн - це все те, що описав Девід Кантер для Haswell . Існує спосіб блокування блокування для прискорення коду, який використовує (і може повернутися до) звичайний замок (особливо з одним блокуванням для всіх елементів контейнера, тому кілька потоків в одному критичному розділі часто не стикаються. ) або написати код, який безпосередньо знає про транзакції.
7.5. Hugepages : анонімні прозорі величезні сторінки добре працюють у Linux, не використовуючи hugetlbfs вручну. Зробіть асигнування> = 2MiB з вирівнюванням 2MiB (наприклад posix_memalign
, або,aligned_alloc
що не примушує дурного вимоги ISO C ++ 17 вийти з ладу, коли size % alignment != 0
).
Анонімний розподіл, вирівняний 2MiB, використовує величезні сторінки за замовчуванням. Деякі навантаження (наприклад, які продовжують використовувати великі асигнування деякий час після їх створення) можуть отримати користь від того,
echo always >/sys/kernel/mm/transparent_hugepage/defrag
щоб ядро дефрагментувати фізичну пам'ять, коли це потрібно, замість того, щоб повертатися до 4-х сторінок. (Див . Документи ядра ). Крім того, використовуйте madvise(MADV_HUGEPAGE)
після великих розмірів (бажано, щоб вони були з вирівнюванням 2MiB).
Додаток B: Oprofile : Linux perf
здебільшого витіснив oprofile
. Для детальних подій, характерних для певних мікроархітектур, використовуйте ocperf.py
обгортку . напр
ocperf.py stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,\
branches,branch-misses,instructions,uops_issued.any,\
uops_executed.thread,idq_uops_not_delivered.core -r2 ./a.out
Деякі приклади його використання див. У розділі Чи може MOV x86 дійсно бути "вільним"? Чому я взагалі не можу це відтворити? .
З мого швидкого огляду це виглядає досить точно. Єдине, що слід помітити, це частина різниці між "інтегрованими" та "зовнішніми" контролерами пам'яті. З моменту випуску ліній i7-процесорів Intel все інтегровано, а AMD використовує інтегровані контролери пам'яті з моменту вперше випуску мікросхем AMD64.
Оскільки ця стаття була написана, не ціла партія змінилася, швидкість зросла вище, контролери пам'яті стали набагато розумнішими (i7 буде затримувати запис в оперативну пам'ять, поки не відчує себе, як здійснити зміни), але не ціла партія змінилася . Принаймні, ні в якому разі не піклується розробник програмного забезпечення.