Энергозависимые переменные C и кэш-память

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

Насколько я понимаю,

  1. Ключевое слово Volatile сообщает компилятору, что ссылки на переменные не должны быть оптимизированы и должны читаться так, как они запрограммированы в коде.

  2. Кэш прозрачно управляется аппаратным кешем, поэтому, когда процессор выдает адрес, он не знает, поступают ли данные из кеша или из памяти.

Итак, если у меня есть требование читать адрес памяти каждый раз, когда это необходимо, как я могу убедиться, что он ссылается не из кеша, а с требуемого адреса?

Каким-то образом эти две концепции плохо сочетаются друг с другом. Уточните, пожалуйста, как это делается.

(Представьте, что у нас есть политика обратной записи в кеш (если это необходимо для анализа проблемы))

Спасибо, Microkernel :)


person Microkernel    schedule 24.10.2011    source источник


Ответы (7)


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

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

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

Более серьезная проблема - это кеширование и согласованность кеш-памяти. Самый простой подход - убедиться, что ваш регистр находится в некэшированном адресном пространстве. Это означает, что каждый раз, когда вы обращаетесь к регистру, вы гарантированно читаете / записываете реальный аппаратный регистр, а не кэш-память. Более сложный, но потенциально более эффективный подход - использовать кэшированное адресное пространство и заставить ваш код вручную принудительно обновлять кеш для конкретных ситуаций, подобных этой. Для обоих подходов, как это достигается, зависит от архитектуры и выходит за рамки вопроса. Это может включать MTRR (для x86), MMU, модификации таблиц страниц и т. Д.

Надеюсь, это поможет. Если я что-то пропустил, дайте мне знать, и я расширю свой ответ.

person Andrew Cottrell    schedule 24.10.2011
comment
Цель volatile при использовании хорошего компилятора должна состоять в том, чтобы гарантировать, что сгенерированный код позволяет процессору знать обо всем, что необходимо написать до определенного момента, и не запрашивать у процессора чтение информации до конца. Программисту также может потребоваться использовать встроенные функции или другие средства для принудительной очистки аппаратного кеша, но принудительная очистка аппаратного кеша будет бесполезна, если компилятор кэширует регистры способами, о которых оборудование ничего не знает. - person supercat; 19.02.2017

Судя по вашему вопросу, вы ошиблись.
Volatile ключевое слово не связано с кешем, как вы описываете.

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

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

Остальное, касающееся кеш-памяти, напрямую к программисту не имеет.

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

person Cratylus    schedule 24.10.2011
comment
Итак, если, как я рассмотрел случай, когда переменная обновляется каким-либо другим потоком или драйвером, считывающим с устройства ввода, какова гарантия того, что я читаю правильное значение, а не что-то кэшированное? Как избежать такого сценария в коде? - person Microkernel; 24.10.2011
comment
Если вы используете volatile, гарантируется, что вы всегда будете читать последнее обновление, которое было выполнено в памяти, из другого потока, но я чувствую, что вас беспокоит больше на уровне ОС, то есть кеш или синхронизация памяти - person Cratylus; 24.10.2011
comment
@Cratylus Если вы используете потоки, последние, прошедшие ... четко не определены между потоками, работающими на ядрах diff. - person curiousguy; 13.11.2019

Я предлагаю пометить страницу как не кэшируемую диспетчером виртуальной памяти.
В Windows это делается с помощью параметра PAGE_NOCACHE при вызове VirtualProtect .

Для несколько иной цели инструкции SSE 2 содержат _mm_stream_xyz инструкции по предотвращению загрязнения кеша, хотя Я не думаю, что они применимы к вашему случаю здесь.

В любом случае не существует переносимого способа делать то, что вы хотите на C; вы должны использовать функциональность ОС.

person user541686    schedule 24.10.2011
comment
Итак, это зависит от платформы? Следовательно, кэш не контролируется аппаратным кешированием? (если оборудование полностью управляет кешем, тогда оно не будет проверять наличие флага PAGE_NOCACHE, верно?) - person Microkernel; 24.10.2011
comment
@Microkernel: он управляется аппаратным обеспечением. Но операционная система сообщает оборудованию, что делать (в конце концов, оборудование не знает, как ОС хочет управлять памятью), и вы просите ОС делать то, что вы хотите. И вся эта информация хранится - угадайте, где? - сама память. Однако это пассивный процесс - ОС вмешивается, только если что-то выходит из строя (например, сбой страницы). В остальном оборудование просто продолжает делать то, что требует ОС, без вмешательства ОС. - person user541686; 24.10.2011
comment
Хм, хорошо ... Кажется, где-то я неправильно понимаю, я всегда считал, что CPU Cache прозрачен для всех, кроме оборудования Cache! Любые ссылки, которые мне нужно прочитать, чтобы понять мои концепции? ! Большое спасибо за разъяснения :) - person Microkernel; 24.10.2011
comment
@Microkernel: Конечно! :) По сути, операционная система хранит всю информацию об управлении памятью в таблицах страниц в памяти и сообщает процессору, где искать информацию. Затем ЦП управляет всем и обращается к операционной системе за помощью, если не может решить, что делать. Вы можете прочитать о разбиении на страницы здесь и о кешировании здесь; дайте мне знать, если у вас остались вопросы. (Вот почему говорят, что операционная система находится между оборудованием и программным обеспечением - это действительно так!) - person user541686; 24.10.2011

В Википедии есть довольно хорошая статья о MTRR (регистрах диапазона типов памяти), которые применяются к семейству x86 ЦП.

Подводя итог, начиная с Pentium Pro, Intel (и скопированный AMD) имел эти регистры MTR, которые могли устанавливать атрибуты без кэширования, сквозной записи, объединения записи, защиты от записи или обратной записи для диапазонов памяти.

Начиная с Pentium III, но, насколько я знаю, действительно полезны только с 64-битными процессорами, они учитывают MTRR, но их можно переопределить с помощью таблиц атрибутов страниц, которые позволяют процессору устанавливать тип памяти для каждой страницы памяти.

Основное использование MTRR, о котором я знаю, - это графическая RAM. Гораздо эффективнее пометить это как объединение записи. Это позволяет кэшу хранить записи и ослабляет все правила упорядочения записи в память, чтобы разрешить очень высокоскоростную пакетную запись на графическую карту.

Но для ваших целей вам может потребоваться установка MTRR или PAT либо без кеширования, либо со сквозной записью.

person Zan Lynx    schedule 24.10.2011

Как вы говорите, кеш прозрачен для программиста. Система гарантирует, что вы всегда будете видеть последнее записанное значение, если вы обращаетесь к объекту через его адрес. «Единственное», что вы можете понести, если в вашем кэше находится устаревшее значение, - это штраф во время выполнения.

person Jens Gustedt    schedule 24.10.2011
comment
Только если у машины только один ЦП. - person JeremyP; 24.10.2011
comment
@JeremyP, я думаю, что вопрос здесь был задан за пределами параллельного доступа к общей памяти. Если у вас есть это в дополнение, да, все становится намного сложнее. Затем вам нужно будет применить соответствующие инструменты для обеспечения согласованности данных. Но тогда это более общая проблема, и рассматривать ее сквозь кеши, вероятно, тоже не совсем правильно. - person Jens Gustedt; 24.10.2011
comment
Не думаю, что это выходило за рамки одновременного доступа к памяти. Предпосылка вопроса заключается в том, что существует есть одновременный доступ к памяти, в противном случае, как вы указываете, кеш прозрачен. - person JeremyP; 24.10.2011
comment
У машины не должно быть более одного процессора. Регистры управления устройствами с отображением в память могут иметь такой же эффект (для аппаратных MCU разработчик может позаботиться о том, чтобы не кэшировать это адресное пространство, для программных ядер на FPGA / PLD, не обязательно). См. Страницу 4 в altera.com/ja_JP/pdfs/literature/ hb / nios2 / n2sw_nii52007.pdf - person Dmitri; 08.10.2015
comment
@JeremyP Только если на машине только один ЦП Это не всегда ложь, но крайне вводит в заблуждение. Его следует читать: только если на машине нет нескольких процессоров, не предназначенных для поддержки потоков. Если ЦП предназначен для поддержки потоков, это гарантировано. - person curiousguy; 13.11.2019

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

  • Сделайте доску, на которой указанные данные не кэшируются. Это может быть уже так, если вы обращаетесь к какому-либо устройству ввода-вывода,
  • Используйте специальные инструкции ЦП, которые обходят кеш. Это используется, когда вам нужно очистить память для активации возможных ошибок SEU.

Детали второго варианта зависят от ОС и / или ЦП.

person mouviciel    schedule 24.10.2011
comment
Я не согласен с этим постом. Ключевое слово volatile просто запрещает компилятору C выполнять определенные оптимизации переменных. Он не ничего делает с кешем. Некоторые компиляторы могут дать вам возможность искажать значение этого ключевого слова (компилятор ARC - один из них), но для большинства компиляторов это не так. - person Jimbo; 03.08.2013

использование ключевого слова _Uncached может помочь во встроенных ОС, таких как MQX

#define MEM_READ(addr)       (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)
person James Zhu    schedule 17.12.2012
comment
Кнопка кода есть не просто так. Пожалуйста, не злоупотребляйте форматированием. - person A--C; 18.12.2012
comment
Какой компилятор поддерживает ключевое слово _Uncached? Поиск в Google для _Uncached дает ваш ответ как первый результат. - person Manuel Jacob; 24.05.2016