вартість атомної експлуатації


90

Яка вартість атомної операції (будь-яке порівняння та обмін або атомне додавання / зменшення)? Скільки циклів він споживає? Це призупинить інші процесори на SMP або NUMA, або заблокує доступ до пам'яті? Чи буде він змивати буфер переупорядкування в непрацюючому ЦП?

Які наслідки матимуть на кеш-пам’яті?

Мене цікавлять сучасні, популярні процесори: x86, x86_64, PowerPC, SPARC, Itanium.


@Jason S, Any. Різниця між cas та атомними inc / dec незначна.
— osgx

2
Атомні операції на x86 стають повільнішими, оскільки більше суперечок розміщується на адресі пам'яті. Я вважаю, що загалом вони на порядок повільніше, ніж незаблокована операція, але явно це буде змінюватися залежно від операції, суперечок та використовуваних бар'єрів пам'яті.
— Стівен Натт,

хммм. пише, здається, атомний на x86. 'Розуміння ядра Linux' -> spin_unlock
— osgx

32-бітна запис є атомною в Java, тобто є портативною атомною (але не має семантики бар'єру пам'яті, тому цього часто недостатньо для покажчиків). Додавання 1 зазвичай не є атомним, якщо ви не додасте префікс LOCK. Щодо ядра Linux, не потрібно дивитись на spin_unlock. Див. У поточних випусках arch / x86 / include / asm / atomic_32.h (раніше це було include / asm-i386 / atomic.h).
— Blaisorblade

@Blaisorblade, JAva тут немає. Яка вартість заблокованих операцій?
— osgx

Відповіді:


59

Я шукав фактичні дані за минулі дні і нічого не знайшов. Однак я провів деякі дослідження, які порівнюють вартість атомних операцій з витратами на пропуск кешу.

Вартість префікса x86 LOCK (включаючи lock cmpxchgатомний CAS) перед PentiumPro (як описано в документі) - це доступ до пам'яті (наприклад, пропуск кешу), + зупинка операцій з пам'яттю іншими процесорами, + будь-яка суперечка з іншими процесорами. намагається заблокувати автобус. Однак, оскільки PentiumPro для звичайної кеш-пам’яті Writeback (вся пам’ять, з якою працює програма, якщо ви не спілкуєтеся безпосередньо з обладнанням), замість блокування всіх операцій з пам’яттю блокується лише відповідний рядок кешу (на основі посилання у відповіді @ osgx ) .

тобто ядро ​​затримує відповідь на запити MESI-спільного доступу та RFO на лінію до закінчення частини фактичної lockоперації редагування. Це називається "блокуванням кешу" і впливає лише на один рядок кешу. Інші ядра можуть одночасно завантажувати / зберігати або навіть обробляти інші лінії.


Насправді, справа CAS може бути більш складною, як пояснюється на цій сторінці , без термінів, зате проникливий опис надійного інженера. (Принаймні для звичайного випадку використання, коли ви виконуєте чисте навантаження до фактичного CAS.)

Перш ніж вдаватися до занадто детальних деталей, я скажу, що заблокована операція коштує одного пропуску кеш-пам’яті + можливої ​​суперечки з іншим процесором на тій самій кеш-лінії, тоді як CAS + попереднє завантаження (що майже завжди потрібно, за винятком мьютексів, де ви завжди CAS 0 та 1) можуть коштувати двох помилок кешу.

Він пояснює, що навантаження + CAS в одному місці може насправді коштувати двох помилок кешу, як Load-Linked / Store-Conditional (див. Там останнє). Його пояснення спирається на знання протоколу когерентності кешу MESI . Він використовує 4 стани для кешеліну: M (одифікований), E (ексклюзивний), S (зайнятий), I (недійсний) (і тому він називається MESI), що пояснюється нижче, де це потрібно. Пояснюваний сценарій такий:

  • Функція LOAD випромінює кеш-пам’ять - відповідна лінія зберігання завантажується з пам'яті у загальному стані (тобто іншим процесорам все ще дозволяється зберігати цю кеш-лінію в пам’яті; в цьому стані зміни не допускаються). Якщо місцезнаходження знаходиться в пам'яті, ця помилка кешу пропускається. Можлива вартість: 1 промах кешу.(пропускається, якщо кеш-лінія знаходиться у загальному, ексклюзивному чи модифікованому стані, тобто дані знаходяться в кеші L1 цього процесора).
  • програма обчислює нові значення для зберігання,
  • і він запускає атомну інструкцію CAS.
    • Він повинен уникати одночасних модифікацій, тому він повинен видалити копії кеш-лінії з кешу інших центральних процесорів, щоб перемістити кеш-лінію в ексклюзивний стан. Можлива вартість: 1 промах кешу. Це не потрібно, якщо воно вже є виключно у власності, тобто в ексклюзивному або модифікованому стані. В обох штатах жоден інший центральний процесор не містить кеш-лінії, але в ексклюзивному стані він ще не змінений (поки).
    • Після цього спілкування змінна змінюється в локальному кеші нашого центрального процесора, після чого вона стає загальновизнаною для всіх інших процесорів (оскільки їх кеші є когерентними з нашими). Зрештою він буде записаний в основну пам’ять за звичайними алгоритмами.
    • Інші процесори, які намагаються прочитати або змінити цю змінну, спочатку повинні отримати цю кеш-лінію в загальному або ексклюзивному режимі, а для цього зв’яжуться з цим процесором та отримають оновлену версію кеш-лінії. Натомість операція LOCKED може коштувати лише пропуску кешу (оскільки кеш-лінія буде запитуватися безпосередньо в ексклюзивному стані).

У всіх випадках запит кеш-лінії може бути зупинений іншими процесорами, які вже змінюють дані.


чому зміна стану на інших процесорах коштує як 1 пропуск кешу?
— osgx

1
Оскільки це зв’язок поза центральним процесором, а отже, повільніший, ніж доступ до кешу. Хоча пропуск кешу все одно повинен передаватися від інших процесорів. Насправді, може бути так, що розмова з іншим центральним процесором є швидшою, ніж розмова з пам'яттю, якщо на найновіших процесорах Xeon використовується пряме взаємозв’язок, наприклад AMD Hypertransport (з часів величезної давності), або Intel QuickPath Interconnect від Intel. на основі Негалема. В іншому випадку зв'язок з іншими процесорами відбувається на тій самій FSB, що і для пам'яті. Шукайте HyperTransport та Front Side Bus у Вікіпедії для отримання додаткової інформації.
— Blaisorblade

Нічого собі, ніколи не думав, що його так дорого - пропуск кешу може тривати кілька тисяч циклів.
— Lothar

2
Справді? Цифра, якою я звик: сто циклів пропусків кеш-пам’яті та тисячі циклів перемикачів контексту / привілеїв (включаючи системні виклики).
— Blaisorblade

1
Пропуск кешу - це не кілька тисяч циклів! Це близько 100 нс, що зазвичай становить 300-350 циклів процесора ....
— user997112

37

Я зробив деяке профілювання з наступним налаштуванням: Тестову машину (AMD Athlon64 x2 3800+) було завантажено, переведено в довгий режим (переривання відключено), а інструкція, що цікавила, виконувалась у циклі, 100 ітерацій розгорнуто та 1000 циклів циклу. Тіло циклу було вирівняно до 16 байт. Час вимірювали за допомогою інструкції rdtsc до і після циклу. Крім того, був виконаний фіктивний цикл без будь-якої інструкції (який вимірював 2 цикли за ітерацію циклу та 14 циклів для решти), а результат був вилучений з результату часу профілювання інструкцій.

Виміряно наступні інструкції:

  • " lock cmpxchg [rsp - 8], rdx" (як із порівнянням, так і з невідповідністю),
  • " lock xadd [rsp - 8], rdx",
  • " lock bts qword ptr [rsp - 8], 1"

У всіх випадках виміряний час становив близько 310 циклів, похибка становила приблизно +/- 8 циклів

Це значення для повторного виконання в одній і тій же (кешованій) пам'яті. З додатковим пропуском кешу час стає значно вищим. Також це було зроблено лише з одним із 2 активних ядер, тому кеш належав виключно у власності, і не потрібно було синхронізації кешу.

Щоб оцінити вартість заблокованої інструкції щодо пропуску кешу, я додав wbinvldінструкцію перед заблокованою інструкцією і помістив wbinvldплюс і add [rsp - 8], raxв цикл порівняння. В обох випадках вартість склала близько 80 000 циклів за пару інструкцій! У випадку блокування bts різниця в часі становила близько 180 циклів за інструкцію.

Зауважте, що це взаємна пропускна здатність, але оскільки заблоковані операції є серіалізаційними, то, мабуть, немає різниці в затримці.

Висновок: заблокована операція важка, але пропуск кешу може бути набагато важчим. Також: заблокована операція не спричиняє помилок кешу. Це може спричинити трафік синхронізації кешу лише тоді, коли кеш-лінія не належить виключно.

Для завантаження машини я використав x64 версію FreeLdr із проекту ReactOS. Ось вихідний код asm:

#define LOOP_COUNT 1000
#define UNROLLED_COUNT 100

PUBLIC ProfileDummy
ProfileDummy:

    cli

    // Get current TSC value into r8
    rdtsc
    mov r8, rdx
    shl r8, 32
    or r8, rax

    mov rcx, LOOP_COUNT
    jmp looper1

.align 16
looper1:

REPEAT UNROLLED_COUNT
    // nothing, or add something to compare against
ENDR

    dec rcx
    jnz looper1

    // Put new TSC minus old TSC into rax
    rdtsc
    shl rdx, 32
    or rax, rdx
    sub rax, r8

    ret

PUBLIC ProfileFunction
ProfileFunction:

    cli

    rdtsc
    mov r8, rdx
    shl r8, 32
    or r8, rax
    mov rcx, LOOP_COUNT

    jmp looper2

.align 16
looper2:

REPEAT UNROLLED_COUNT
    // Put here the code you want to profile
    // make sure it doesn't mess up non-volatiles or r8
    lock bts qword ptr [rsp - 8], 1
ENDR

    dec rcx
    jnz looper2

    rdtsc
    shl rdx, 32
    or rax, rdx
    sub rax, r8

    ret

Дякую! Чи можете ви опублікувати свій тестовий код або протестувати Core2 / Core i3 / i5 / i7 самостійно? Чи всі ядра були ініціалізовані у вашому тестовому налаштуванні?
— osgx

Я додав вихідний код. Ініціалізовано лише одне ядро. Хотілося б побачити результати від інших машин.
— Тимо

CLFLUSH повинен бути набагато легшим способом очищення рядка кешу, ніж WBINVD усього кешу. WBINVD також очистить кеш інструкцій, що призведе до додаткових помилок кешу.
— Пітер Кордес,

Можливо, цікаво протестувати випадок гарячої лінії кешу в Shared state. Ви можете зробити це, якщо інший потік прочитає його з чистим навантаженням.
— Пітер Кордес,

4

На SMP на основі шини атомний префікс LOCKстверджує (вмикає) сигнал дротової шини LOCK#. Він забороняє використовувати інші шини / пристрої на шині.

PPRO & P2 книга http://books.google.com/books?id=3gDmyIYvFH4C&pg=PA245&dq=lock+instruction+pentium&lr=&ei=_E61S5ehLI78zQSzrqwI&cd=1#v=onepage&q=lock%20instruction%20pentium&f=false сторінки 244-246

Заблоковані інструкції - це серіалізація, синхронізація операцій .... / про Невпорядкований / заблокований RMW / read-modify-write = atomic сам / інструкція гарантує, що процесор виконуватиме всі інструкції до заблокованої інструкції до її виконання. / про ще не змиті записи / він змушує всі розміщені записи в процесорі переноситись у зовнішню пам’ять перед виконанням наступної інструкції.

/ про SMP / семафор знаходиться в кеші в штаті S ... видає транзакцію зчитування та анулювання на 0 байт дати (це вбивство / спільних копій рядка кешу в сусідніх центральних процесорах /)

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