[c++] C ++ 11 представил стандартизованную модель памяти. Что это значит? И как это повлияет на программирование на С ++?



Answers

Я просто дам аналогию, с которой я понимаю модели согласованности памяти (или модели памяти, для краткости). Его вдохновляет оригинальная статья Лесли Лампорта «Время, часы и порядок событий в распределенной системе» . Аналогия уместна и имеет фундаментальное значение, но может быть излишним для многих людей. Однако я надеюсь, что это дает мысленный образ (графическое представление), что облегчает рассуждение о моделях согласованности памяти.

Давайте рассмотрим истории всех мест памяти в диаграмме пространства-времени, в которой горизонтальная ось представляет адресное пространство (т. Е. Каждая ячейка памяти представлена ​​точкой на этой оси), а вертикальная ось представляет время (мы увидим, что, в общем, нет универсального понятия времени). Таким образом, история значений, хранящихся в каждой ячейке памяти, представлена ​​вертикальным столбцом по этому адресу памяти. Каждое изменение значения связано с тем, что один из потоков записывает новое значение в это место. Под изображением памяти мы будем понимать совокупность / комбинацию значений всех мест памяти, наблюдаемых в определенный момент времени конкретным потоком .

Цитата из «Праймера по согласованности памяти и согласованности кеша»

Интуитивная (и наиболее ограничительная) модель памяти представляет собой последовательную согласованность (SC), в которой многопоточное выполнение должно выглядеть как чередование последовательных исполнений каждого составного потока, как если бы потоки были мультиплексированы по времени на одноядерном процессоре.

Этот глобальный порядок памяти может варьироваться от одного запуска программы к другому и может быть не известен заранее. Характерной особенностью SC является набор горизонтальных срезов в диаграмме адрес-пространство-время, представляющий плоскости одновременности (т. Е. Изображения памяти). На данной плоскости все его события (или значения памяти) являются одновременными. Существует понятие Абсолютного времени , в котором все нити согласуются с тем, какие значения памяти являются одновременными. В SC в каждый момент времени есть только один образ памяти, общий для всех потоков. То есть, в каждый момент времени все процессоры согласуют изображение с памятью (т. Е. Совокупное содержимое памяти). Это не только означает, что все потоки рассматривают одну и ту же последовательность значений для всех мест памяти, но также и то, что все процессоры выполняют одни и те же комбинации значений всех переменных. Это то же самое, что сказать, что все операции с памятью (во всех ячейках памяти) наблюдаются в одном и том же порядке всеми потоками.

В моделях с ослабленной памятью каждый поток будет по-своему срезать адрес-пространство-время, единственным ограничением является то, что срезы каждого потока не должны пересекаться друг с другом, потому что все потоки должны согласовывать историю каждого отдельного места памяти (конечно , срезы разных потоков могут и будут пересекаться друг с другом). Нет универсального способа разрезать его (без привилегированного слоения адресного пространства-времени). Ломтики не должны быть плоскими (или линейными). Они могут быть изогнутыми, и это то, что может сделать значения чтения потока, написанные другим потоком, из того, в каком они были записаны. Истории разных мест памяти могут скользить (или растягиваться) произвольно относительно друг друга, если смотреть на какой-либо конкретный поток , Каждый поток будет иметь другое представление о том, какие события (или, что то же самое, значения памяти) являются одновременными. Набор событий (или значений памяти), которые одновременно связаны с одним потоком, не являются одновременными с другими. Таким образом, в модели с ослабленной памятью все потоки по-прежнему сохраняют одну и ту же историю (т.е. последовательность значений) для каждой ячейки памяти. Но они могут наблюдать разные образы памяти (т. Е. Комбинации значений всех мест памяти). Даже если два разных места памяти записываются одним и тем же потоком в последовательности, два новых записанных значения могут наблюдаться в другом порядке другими потоками.

[Иллюстрация из Википедии]

Читатели, знакомые с специальной теорией относительности Эйнштейна , заметят, о чем я говорю. Перевод слов Минковского в область моделей памяти: адресное пространство и время - это тени адресного пространства-времени. В этом случае каждый наблюдатель (т. Е. Поток) будет проектировать тени событий (т. Е. Запоминает память / нагрузки) на свою собственную линию мира (т. Е. Свою временную ось) и свою собственную плоскость одновременности (его ось адресного пространства) , Темы в модели памяти C ++ 11 соответствуют наблюдателям , которые движутся относительно друг друга в специальной теории относительности. Последовательная согласованность соответствует галилеевскому пространству-времени (т. Е. Все наблюдатели соглашаются на один абсолютный порядок событий и глобальное чувство одновременности).

Сходство между моделями памяти и специальной теорией относительности связано с тем, что оба определяют частично упорядоченный набор событий, часто называемый причинным множеством. Некоторые события (т. Е. Хранилища памяти) могут влиять (но не влиять) на другие события. Нить C ++ 11 (или наблюдатель в физике) - это не более чем цепочка (т. Е. Полностью упорядоченный набор) событий (например, загрузка памяти и сохранение на разные адреса).

В теории относительности некоторый порядок восстанавливается на кажущуюся хаотическую картину частично упорядоченных событий, поскольку единственным временным порядком, с которым согласны все наблюдатели, является упорядочение между «временными» событиями (т. Е. Те события, которые в принципе соединяются с любой частицей, идущей медленнее чем скорость света в вакууме). Только упорядоченные по времени события инвариантно упорядочены. Время в физике, Крейг Каллендер .

В модели памяти C ++ 11 аналогичный механизм (модель согласованности-освобождения-выпуска) используется для установления этих локальных отношений причинности .

Чтобы дать определение последовательности памяти и мотивации отказа от SC, я приведу цитату из «Праймера по согласованности памяти и согласованности кеша»,

Для компьютера с общей памятью модель согласованности памяти определяет архитектурно видимое поведение своей системы памяти. Критерий правильности одного ядра процессора разбивает поведение между « одним правильным результатом » и « множеством неправильных альтернатив ». Это связано с тем, что архитектура процессора предусматривает, что выполнение потока преобразует заданное входное состояние в одно четко определенное состояние вывода даже на ядре вне порядка. Однако модели согласованности с общей памятью относятся к нагрузкам и хранилищам нескольких потоков и обычно позволяют много правильных исполнений , не допуская многих (более) неправильных. Возможность множественных правильных исполнений обусловлена ​​тем, что ISA позволяет одновременному выполнению нескольких потоков, часто со многими возможными законными перемежениями команд из разных потоков.

Расслабленные или слабые модели согласованности памяти мотивированы тем, что большинство упорядочений памяти в сильных моделях не нужно. Если поток обновляет десять элементов данных, а затем флаг синхронизации, программистам обычно не важно, обновлены ли элементы данных по порядку относительно друг друга, а только обновлены все элементы данных до обновления флага (обычно они реализуются с использованием инструкции FENCE ). Расслабленные модели стремятся уловить эту повышенную гибкость порядка и сохранить только заказы, которые программисты « требуют », чтобы получить как более высокую производительность, так и правильность SC. Например, в некоторых архитектурах буферы записи FIFO используются каждым ядром для хранения результатов фиксированных (удаленных) хранилищ перед тем, как записывать результаты в кеши. Эта оптимизация повышает производительность, но нарушает SC. Буфер записи скрывает задержку обслуживания пропусков магазина. Поскольку магазины являются общими, возможность избежать остановки большинства из них является важным преимуществом. Для одноядерного процессора буфер записи может быть сделан архитектурно невидимым, гарантируя, что загрузка адреса A возвращает значение самого последнего хранилища в A, даже если один или несколько хранилищ для A находятся в буфере записи. Обычно это делается путем обхода значения самого последнего хранилища в A до нагрузки от A, где «последнее» определяется порядком программы или путем остановки нагрузки A, если хранилище A находится в буфере записи , Когда используется несколько ядер, каждый из них будет иметь свой собственный байпас записи. Без буферов записи аппаратное обеспечение является SC, но с буферами записи это не так, что делает буферы записи архитектурно видимыми в многоядерном процессоре.

Переупорядочение магазина-хранилища может произойти, если в ядре есть буфер записи, отличный от FIFO, который позволяет магазинам уходить в другом порядке, чем в том порядке, в котором они были введены. Это может произойти, если первый магазин промахивается в кеше, а второй - или если второй магазин может объединиться с более ранним хранилищем (то есть перед первым хранилищем). Переупорядочение нагрузки может также происходить в динамически запланированных ядрах, которые выполняют инструкции из программы. Это может вести себя так же, как переупорядочение магазинов на другом ядре (можете ли вы придумать пример чередования между двумя потоками?). Переупорядочение ранней загрузки с последующим хранилищем (переупорядочение хранилища-загрузки) может привести к множеству неправильных действий, таких как загрузка значения после отпускания блокировки, которая его защищает (если хранилище является операцией разблокировки). Обратите внимание, что переупорядочивание в хранилище может также возникать из-за локального обхода в обычно реализованном буфере записи FIFO даже с ядром, которое выполняет все команды в порядке выполнения программы.

Поскольку согласованность кэша и согласованность памяти иногда путают, поучительно также иметь эту цитату:

В отличие от последовательности, когерентность кэша не является видимой для программного обеспечения и не требуется. Когерентность направлена ​​на то, чтобы кэши системы с разделяемой памятью были функционально невидимы как кеши в одноядерной системе. Правильная согласованность гарантирует, что программист не может определить, имеет ли и где система кэширует, анализируя результаты нагрузок и хранилищ. Это связано с тем, что правильная согласованность гарантирует, что кеши никогда не будут включать новое или другое функциональное поведение (программисты могут все еще иметь возможность вывести вероятную структуру кэша с использованием информации о времени ). Основная цель протоколов когерентности кеша - поддерживать инвариант одиночного писателя-множественного считывателя (SWMR) для каждой ячейки памяти. Важным различием между согласованностью и согласованностью является то, что когерентность указана для каждой ячейки памяти , тогда как согласованность указана для всех мест памяти.

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

Question

C ++ 11 представил стандартизованную модель памяти, но что именно это означает? И как это повлияет на программирование на С ++?

Эта статья ( Гэвин Кларк , цитирующая Херба Саттера ) говорит, что,

Модель памяти означает, что код C ++ теперь имеет стандартизованную библиотеку для вызова независимо от того, кто создал компилятор и на какой платформе он работает. Существует стандартный способ управления тем, как разные потоки разговаривают с памятью процессора.

«Когда вы говорите о разделении [кода] на разные ядра, которые находятся в стандарте, мы говорим о модели памяти. Мы собираемся ее оптимизировать, не нарушая следующих предположений, которые люди собираются сделать в коде», - сказал Саттер .

Ну, я могу запомнить этот и аналогичные абзацы, доступные в Интернете (так как у меня была моя собственная модель памяти с момента рождения: P), и я могу даже написать ответ на вопросы, заданные другими, но, честно говоря, я не совсем понимаю это ,

Итак, что я в основном хочу знать, программисты на C ++ раньше разрабатывали многопоточные приложения, поэтому как это важно, если это потоки POSIX или потоки Windows или потоки C ++ 11? Каковы преимущества? Я хочу понять детали низкого уровня.

Я также чувствую, что модель памяти C ++ 11 каким-то образом связана с поддержкой многопоточности C ++ 11, поскольку я часто вижу их вместе. Если да, то как именно? Почему они должны быть связаны?

Поскольку я не знаю, как работают работы многопоточности и какая модель памяти в целом, пожалуйста, помогите мне понять эти понятия. :-)




Это означает, что стандарт теперь определяет многопоточность и определяет, что происходит в контексте нескольких потоков. Конечно, люди использовали разные реализации, но это похоже на вопрос, почему у нас должна быть std::string когда мы все могли бы использовать класс, состоящий из string .

Когда вы говорите о потоках POSIX или потоках Windows, это немного иллюзия, поскольку на самом деле вы говорите о потоках x86, так как это аппаратная функция, выполняемая одновременно. Модель памяти C ++ 0x предоставляет гарантии, независимо от того, находитесь ли вы на x86 или ARM или MIPS или что-то еще, что вы можете придумать.




Если вы используете мьютексы для защиты всех ваших данных, вам действительно не нужно беспокоиться. Мьютекс всегда обеспечивал достаточные гарантии заказа и видимости.

Теперь, если вы использовали атоматику или алгоритмы блокировки, вам нужно подумать о модели памяти. Модель памяти точно описывает, когда атомистика обеспечивает гарантии порядка и видимости, а также предоставляет переносные ограждения для ручных гарантий.

Раньше атомизация выполнялась с использованием встроенных компиляторов или некоторой библиотеки более высокого уровня. Заборы были бы выполнены с использованием инструкций, специфичных для процессора (барьеров памяти).






Related