В стандарте C++11 определяет модель памяти (1.7, 1.10), которое содержит память порядков, которые, грубо говоря, "и последовательно-последовательный на", то "приобрести" В, С "использование и", и"Освобождение", и на "услуги" по. Одинаково примерно, программа правильная, только если это гонки-бесплатная, что произойдет, если все действия можно положить в некоторый порядок, в котором одно действие бывает-до еще один. Так что действия X происходит-до акции г в Х расположено-перед г (в одну нить), или Х Интер-нить происходит-перед г. Последнее условие, учитывая, в частности, когда
Синхронизация-с происходит, когда Х атомная магазине с "Освобождение" и заказ на некоторой атомарной переменной и Г атомная нагрузки с "приобрести" и заказ на одну и ту же переменную. Будучи зависимость-заказал-до бывает в аналогичной ситуации, когда г загрузить с "потреблять" на заказ (и соответствующей памяти). Понятие синхронизация-с расширяет бывает-до отношение транзитивно через действия, секвенирован-перед другом в потоке, но, будучи зависимость-заказал-до распространяется транзитивно только через строгое подмножество секвенирован-до называется несет-зависимость, которое следует довольно большой набор правил, и, в частности, могут быть прерваны с СТД::kill_dependency`.
Итак, в чем смысл понятия "на заказ зависимость" и? Какое преимущество он обеспечивает более проще секвенирован-до / синхронизация-с Заказ? Поскольку правила за это строже, я предполагаю, что могут быть реализованы более эффективно.
Вы можете привести пример программы, где переход от выпуска/приобретения на отпуск/потреблять является правильным и обеспечивает нетривиальное преимущество? И когда СТД::kill_dependency даст улучшение? Высокого уровня аргументы было бы неплохо, но бонусные баллы за особые аппаратные различия.
Зависимость данных заказ был введен N2492 со следующим обоснованием:
есть два важных случаях, когда текущий рабочий проект (N2461) не поддерживает масштабируемость рядом, что возможно на некоторые существующие аппаратные средства.
- доступ на чтение редко написано параллельных структур данных
очень редко написано параллельные структуры данных являются довольно распространенным явлением, как в операционной системе ядер и в серверных приложениях. Примеры структур данных, представляющих за пределами государства (такие как таблицы маршрутизации), настройка программного обеспечения (в настоящий момент загруженных модулей), настройка оборудования (устройства хранения данных, используемых в настоящее время), и политики безопасности (права доступа, правила брандмауэра). Читать-писать показатели, намного превышающие миллиард к одному, достаточно распространены.
- публикации-подписки семантику указателя-опосредованной публикации
много общения между потоками указатель-опосредованный, на котором производитель публикует указатель, с помощью которого потребитель может получить доступ к информации. Доступ к этим данным возможен без полного приобретают семантику.
В таких случаях использование Интер-поток данных-зависимость заказ привел в порядок величин ускорений и тому подобные улучшения в масштабируемости на машинах, которые поддерживают между потоками данных зависимостей заказ. Такие результаты становятся возможными потому, что таких машин может избежать дорогостоящих приобретений блокировки, атомарные инструкции, или памяти заборы, которые не требуют.
выделено мной
мотивации использования представленного там `rcu_dereference () из ядра Linux
Джефф Preshing имеет большой пост в блоге, отвечая на этот вопрос. Я могу'т что-то добавить от себя, но думаю, что всем интересно о потреблять и приобретать, прочитайте его пост:
http://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/
Он показывает конкретный пример на C++ с соответствующим эталонным ассемблерный код в трех разных архитектур. По сравнению с memory_order_acquire
, memory_order_consume
потенциально обладает 3-кратным ускорением на PowerPC, 1,6 х ускорение на руку, и незначительное ускорение на x86, который все равно твердая консистенция. Загвоздка в том, что, как, когда он писал это, только ССЗ действительно лечат потреблять семантика отличается от приобретения, и, вероятно, из-за ошибки. Тем не менее, это показывает, что ускорение возможно, если писатели компилятор может выяснить, как воспользоваться ею.
Нагрузки потребляют очень похожа нагрузки приобретают, за исключением того, что оно вызывает происходит-раньше отношения только к выражению оценок, данных-зависит от нагрузки потребляют. Накрутка выражение С результаты kill_dependency
в значение, которое больше не несет в себе зависимость от нагрузки,-потреблять.
Основное использование это для писателя, чтобы построить структуру данных последовательно, затем отбросить общий указатель на структуру (с помощью освобождение
или acq_rel
атомной). Читатель использует нагрузки-потреблять, чтобы прочитать указатель, и разыменовывает его, чтобы получить структуру данных. В разыменовать создает зависимость данных, поэтому читатель гарантированно увидеть инициализированные данные.
std::atomic<int *> foo {nullptr};
std::atomic<int> bar;
void thread1()
{
bar = 7;
int * x = new int {51};
foo.store(x, std::memory_order_release);
}
void thread2()
{
int *y = foo.load(std::memory_order_consume)
if (y)
{
assert(*y == 51); //succeeds
// assert(bar == 7); //undefined behavior - could race with the store to bar
// assert(kill_dependency(*y) + bar == 58) // undefined behavior (same reason)
assert(*y + bar == 58); // succeeds - evaluation of bar pulled into the dependency
}
}
Есть две причины для обеспечения нагрузки потребляют. Основная причина заключается в том, что рука и силовых нагрузок гарантированно потреблять, но требуется дополнительное ограждение, чтобы превратить их в приобретает. (На x86, все нагрузки приобретает, поэтому потреблять обеспечивает прямой преимущество в производительности в наивных компиляции.) Вторая причина заключается в том, что компилятор может перемещать впоследствии операции без данных от до потреблять, что он может'т сделать для приобретения. (Включение таких оптимизаций является большая причина для построения всех эта память заказ на язык.)
Накрутка значение с kill_dependency
позволяет вычисление выражения, которое зависит от величины должны быть перемещены, чтобы до нагрузки потреблять. Это полезно, например, когда значение является индексом в массиве, которая была ранее читал.
Обратите внимание, что использование потреблять результаты в происходит-перед отношением, что больше не транзитивные (хотя он по-прежнему гарантируется ациклические). Например, в магазине " бар " происходит до магазина на Foo, который происходит перед тем, как разыменовать из Г
, который происходит до чтения бар
(в закомментированных утверждать), но в магазине для бар
не'т случится раньше, чем читать о БАР
. Это приводит к гораздо более сложное определение происходит-раньше, но вы можете себе представить, как это работает (начните с последовательного-до, а затем распространяется через какой-либо номер выпуска-потреблять-dataDependency или освобождение-захват-sequencedBefore ссылки)
Я'd как записать частичном поиске, хотя это's не реальный ответ, а не'Т означает, что там выиграл'т быть большая награда для правильного ответа.
После глядя на 1.10 на некоторое время, и, в частности, очень полезным, обратите внимание, в пункте 11, я думаю, это'т на самом деле так тяжело. Большая разница между синхронизация-с (в дальнейшем: З/Ж) и зависимость-заказал-до (Дата рождения) состоит в том, что происходит-перед отношения могут быть созданы путем объединения секвенирован-до (С/Б) и х/б произвольно, но не так для доб. Обратите внимание, одно из определений для межпоточно происходит до:
а
синхронизируется-сX
иX
расположено перед "Б"
Но аналогичные заявление на <ы> В " а " зависимость-заказал перед X
</с> отсутствует!
Так с выпуска/приобретения (т. е. С/Ш) мы можете заказать произвольных событий:
в
A1 s/b B1 Thread 1
s/w
C1 s/b D1 Thread 2
Но теперь рассмотрим произвольную последовательность событий такой:
в
A2 s/b B2 Thread 1
dob
C2 s/b D2 Thread 2
В этом sequenece, это все-таки правда, что " А2 "бывает-до С2
(потому что " А2 " с/б В2
и В2*межпоточно происходит перед*
С2на счет др, но мы могли бы утверждать, что вы можете никогда не говорите!). Однако, **неправда**, что
А2*бывает-до*
Д2. События
А2и
Д2не упорядочены по отношению друг к другу, **если** он на самом деле считает, что
С2*коротые в*
Д2. Это более строгое требование, и в отсутствие такого требования,
А2-к-
Д2` не может быть заказан на "через" в отпуск/потреблять пара.
Иными словами, выброс/потреблять пара распространяется только порядок действий, которые влекут за собой зависимость от одного к другому. Все, что's не зависимый не заказывала через выпуск/потреблять пара.
Кроме того, обратите внимание, что заказ будет восстановлена, если мы добавляем в финал, сильным выпуска/приобретения пары:
в
A2 s/b B2 Th 1
dob
C2 s/b D2 Th 2
s/w
E2 s/b F2 Th 3
Теперь, процитировал правило, Д2
межпоточно происходит перед Ф2
, и поэтому так делать С2
и В2
, так и А2
бывает-до Ф2
. Но учтите, что до сих пор нет заказ между А2
и Д2
&ампер;mdash; не на заказ только между " А2 " и поздно события.
В резюме и в заключение, неся зависимость является строгим подмножеством общей последовательности, и выпустить/потреблять пар дают заказ только действия, которые несут зависимостей. Покуда нет сильнее заказ (например, проходя через выпуска/приобретения пары), там теоретически потенциал для дополнительной оптимизации, так как все, что не цепочки зависимостей могут быть свободно перераспределены.
Может быть, вот пример, который имеет смысл?
std::atomic<int> foo(0);
int x = 0;
void thread1()
{
x = 51;
foo.store(10, std::memory_order_release);
}
void thread2()
{
if (foo.load(std::memory_order_acquire) == 10)
{
assert(x == 51);
}
}
Как написано, код гонки-бесплатные и утверждение состоится, потому что релиз/приобрести пару orderes магазине х = 51
до нагрузки в утверждение. Однако, меняется на "приобрести" и в "потреблять" Ну, это не перестанет быть правдой и программы бы в данные гонки на х
, с Х = 51
несет никакой зависимостей в магазин в Foo
. Смысл оптимизации заключается в том, что этому магазину можно свободно переставлять, не опасаясь, что Foo-это делать, потому что нет зависимости.