Ограничения за подреждане на паметта на x86 архитектура

В своята велика книга „C++ Concurrency в действие“ Антъни Уилямс пише следното (страница 309):

Например при архитектури x86 и x86-64 операциите за атомарно зареждане са винаги еднакви, независимо дали са маркирани memory_order_relaxed или memory_order_seq_cst (вижте раздел 5.3.3). Това означава, че кодът, написан с помощта на облекчено подреждане на паметта, може да работи на системи с x86 архитектура, където би се провалил на система с по-подробен набор от инструкции за подреждане на паметта, като SPARC.

Правилно ли разбирам, че на x86 архитектура всички операции за атомно зареждане са memory_order_seq_cst? Освен това на сайта cppreference std::memory_order се споменава, че при x86 освобождаване-aquire подреждане е автоматичен.

Ако това ограничение е валидно, подрежданията все още ли се прилагат за оптимизациите на компилатора?


person Stephan Dollberg    schedule 10.05.2012    source източник
comment
всички операции за атомно зареждане са memory_order_seq_cst? дори не е грешно, това е безсмислено твърдение. Никоя операция не е или не е поръчка. Операциите в програмата са.   -  person curiousguy    schedule 12.12.2019


Отговори (5)


Да, подреждането все още се прилага за оптимизациите на компилатора.

Освен това не е съвсем точно, че на x86 "операциите за атомарно зареждане са винаги еднакви".

На x86 всички зареждания, извършени с mov, имат семантика за придобиване и всички съхранявания, извършени с mov, имат семантика за освобождаване. Така че acq_rel, acq и отпуснатите натоварвания са прости movs и по подобен начин acq_rel, rel и отпуснатите магазини (acq запасите и rel натоварванията винаги са равни на отпуснати).

Това обаче не е непременно вярно за seq_cst: архитектурата не гарантира семантика на seq_cst за mov. Всъщност наборът от инструкции x86 няма специфични инструкции за последователно последователно зареждане и съхраняване. Само атомарните операции за четене-модифициране-запис на x86 ще имат seq_cst семантика. Следователно можете да получите seq_cst семантика за товари, като извършите операция fetch_and_add (lock xadd инструкция) с аргумент 0 и seq_cst семантика за магазини, като извършите seq_cst обменна операция (xchg инструкция) и отхвърлите предишната стойност.

Но не е нужно да правите и двете! Докато всички запаметявания на seq_cst се извършват с xchg, зарежданията на seq_cst могат да бъдат реализирани просто с mov. Двойно, ако всички зареждания бяха извършени с lock xadd, съхраняванията на seq_cst биха могли да бъдат реализирани просто с mov.

xchg и lock xadd са много по-бавни от mov. Тъй като една програма има (обикновено) повече зареждания, отколкото съхранява, удобно е да се правят seq_cst съхранява с xchg, така че (по-честите) seq_cst зареждания могат просто да използват mov. Тази подробност на изпълнението е кодифицирана в x86 Application Binary Interface (ABI). На x86 съвместимият компилатор трябва да компилира seq_cst съхранява в xchg, така че зарежданията на seq_cst (които може да се появят в друга преводна единица, компилирана с различен компилатор) да могат да се извършват с по-бързата mov инструкция.

Следователно не е вярно като цяло, че зарежданията на seq_cst и придобиването се извършват с една и съща инструкция на x86. Вярно е само защото ABI указва, че хранилищата seq_cst се компилират до xchg.

person Paolo Bonzini    schedule 29.08.2013
comment
Само атомарните операции за четене-модифициране-запис на x86 ще имат seq_cst семантика. Не, те нямат. Това е безсмислено твърдение. Само изпълнението на програмата може да бъде последователно, инструкцията не може. - person curiousguy; 12.12.2019
comment
@curiousguy: по-точно: само заключените операции за четене-модифициране-запис на x86 ще имат семантика, съответстваща на C11 атомарни операции с подреждане seq_cst. зарежданията ще имат семантика, съответстваща на подреждането на получаване на C11, и съхраняването на подреждането на освобождаване на C11. - person Paolo Bonzini; 16.01.2020

Разбира се, компилаторът трябва да следва правилата на езика, независимо от хардуера, на който работи.

Това, което той казва, е, че на x86 нямате облекчено подреждане, така че получавате по-строго подреждане, дори и да не го поискате. Това също означава, че такъв код, тестван на x86, може да не работи правилно на система, която има облекчен ред.

person Bo Persson    schedule 10.05.2012

Струва си да се има предвид, че въпреки че load relaxed и seq_cst load могат да се съпоставят с една и съща инструкция на x86, те не са еднакви. Отпуснатото натоварване може свободно да бъде пренаредено от компилатора през операции с памет към различни места в паметта, докато натоварването seq_cst не може да бъде пренаредено през други операции с памет.

person briand    schedule 03.09.2013

Изречението от книгата е написано донякъде подвеждащо. Подреждането, получено в дадена архитектура, зависи не само от това как превеждате атомни натоварвания, но и от това как превеждате атомни магазини.

Обичайният начин за внедряване на seq_cst на x86 е да се изчисти буферът на магазина в някакъв момент между всеки seq_cst магазин и последващо seq_cst зареждане от същата нишка. Обичайният начин компилаторът да гарантира това е да изчисти след съхраняване, тъй като има по-малко магазини, отколкото зареждания. В този превод seq_cst зарежданията не трябва да се флъшват.

Ако програмирате x86 само с обикновени зареждания и съхранявания, зарежданията гарантирано осигуряват acquire семантика, а не seq_cst.

Що се отнася до оптимизацията на компилатора, в C11/C++11 компилаторът прави оптимизации в зависимост от движението на кода въз основа на семантиката на конкретните атоми, преди да вземе предвид основния хардуер. (Хардуерът може да осигури по-силно подреждане, но няма причина компилаторът да ограничава своите оптимизации поради това.)

person user2949652    schedule 07.11.2013
comment
Обърнете внимание, че трябва да промивате само между склад и товар; няколко последователни операции в магазина не се нуждаят от ограда между тях. Ако вашият codegen е за всяка атомна операция в изолация, не можете да постигнете това, разбира се. - person curiousguy; 12.12.2019

Правилно ли разбирам, че на x86 архитектура всички операции за атомно зареждане са memory_order_seq_cst?

Само изпълненията (на програма, на някои видими междунишкови операции в програма) могат да бъдат последователни. Една единствена операция сама по себе си не е последователна.

Да се ​​пита дали изпълнението на една изолирана операция е последователно е безсмислен въпрос.

Преводът на всички операции с памет, които се нуждаят от някаква гаранция, трябва да се извърши следвайки стратегия, която позволява тази гаранция. Може да има различни стратегии, които имат различни разходи за сложност на компилатора и разходи за време на изпълнение.

[Само че има различни стратегии за внедряване на виртуални функции: единствената, която е ОК (която отговаря на всичките ни очаквания за скорост, предвидимост и простота) е използването на vtables, така че всички компилатори използват vtable, но виртуална функция не е дефинирана като преминаване през vtable.]

На практиката има не много различни стратегии, използвани за прилагане на memory_order_seq_cst операции на даден CPU (за които знам). Разликите между компилаторите са малки и не възпрепятстват бинарната съвместимост. Но има потенциални разлики и усъвършенстваната глобална оптимизация на многонишкови програми може да отвори нови възможности за по-ефективно генериране на код за атомарни операции.

В зависимост от вашия компилатор, програма, която съдържа само облекчени зареждания и memory_order_seq_cst модификации на std::atomic<> обекти, може или не може да има само последователно поведение, дори на силно подреден CPU.

person curiousguy    schedule 12.12.2019