ЦП (конкретно його контролер пам'яті) може скористатися тим, що пам'ять не мутується
Перевага полягає в тому, що цей факт рятує компілятор від використання інструкцій з мембрани, коли доступ до даних.
Бар'єр пам’яті, також відомий як мембрана, огорожа пам’яті чи інструкція щодо забору, є типом бар'єрної інструкції, яка викликає центральний процесорний блок (ЦП) або компілятор, щоб застосувати обмеження для замовлення на операції з пам’яттю, видані до і після вказівки щодо бар’єру. Зазвичай це означає, що певні операції гарантовано виконуються перед бар'єром, а інші - після.
Бар'єри пам’яті необхідні, оскільки більшість сучасних процесорів використовують оптимізацію продуктивності, що може призвести до виконання поза замовленням. Таке упорядкування операцій із пам’яттю (завантаження та зберігання) зазвичай залишається непоміченим у межах однієї нитки виконання, але може спричинити непередбачувану поведінку у паралельних програмах та драйверах пристроїв, якщо ретельно не контролюватись ...
Розумієш, коли доступ до даних відбувається з різних потоків, у багатоядерному процесорі це відбувається наступним чином: різні потоки працюють у різних ядрах, кожен з яких використовує свій власний (локальний для свого ядра) кеш - копію деякого глобального кешу.
Якщо дані є змінними і програмісту потрібно, щоб вони узгоджувались між різними потоками, необхідно вжити заходів для гарантування узгодженості. Для програміста це означає використання конструкцій синхронізації, коли вони отримують доступ (наприклад, для читання) даних у конкретному потоці.
Для компілятора конструкція синхронізації в коді означає, що потрібно вставити інструкцію з мембрани , щоб переконатися, що зміни, внесені до копії даних на одному з ядер, належним чином розповсюджуються ("публікуються"), щоб гарантувати кеш-пам'ять на інших ядрах мати однакову (сучасну) копію.
Дещо спрощуючи див. Примітку нижче , ось що відбувається у багатоядерному процесорі для мембрани:
- Усі ядра припиняють обробку - щоб уникнути випадкового запису в кеш.
- Усі оновлення локальних кешів списуються у глобальний - щоб гарантувати, що глобальний кеш містить найсвіжіші дані. Це займає певний час.
- Оновлені дані списуються з глобального кеша в локальний - щоб локальні кеші містили останні дані. Це займає певний час.
- Усі сердечники відновлять виконання.
Розумієте, усі ядра нічого не роблять, коли дані копіюються туди-сюди між глобальними та локальними кешами . Це необхідно для забезпечення правильної синхронізації змінних даних (безпечно для потоків). Якщо є 4 ядра, усі 4 зупиняються і чекають синхронізації кешів. Якщо їх 8, всі 8 зупиняються. Якщо їх 16 ... ну, у вас 15 ядер, які точно нічого не роблять, чекаючи речі, необхідні для виконання на одному з них.
Тепер давайте подивимося, що станеться, коли дані незмінні? Незалежно від того, яка нитка звертається до неї, вона гарантовано буде однаковою. Для програміста це означає, що немає необхідності вставляти конструкції синхронізації, коли вони отримують доступ (читають) дані в конкретному потоці.
Для компілятора це, в свою чергу, означає, що не потрібно вставляти інструкцію з мембрани .
Як результат, доступ до даних не потрібно зупиняти ядра та чекати, коли дані записуються назад і назад між глобальними та локальними кешами. Це перевага в тому, що пам'ять не вимкнено .
Зауважте, що дещо спрощене пояснення вище падає на деякі складніші негативні наслідки зміни даних, наприклад, на конвеєрі . Для того, щоб гарантувати необхідне замовлення, центральний процесор повинен визнати недійсними стовпчики, на які впливає зміна даних - це ще одне покарання за ефективність. Якщо це реалізується шляхом прямої (і таким чином надійної :) недійсності всіх трубопроводів, то негативний ефект ще більше посилюється.