Аппаратные способы решения проблемы когерентности
Большинство из предложенных способов борьбы с некогерентностью ориентированы на динамическое (в процессе вычислений) распознавание и устранение несогласованности копий совместно используемых данных с помощью специальной аппаратуры. Аппаратные методы обеспечивают более высокую производительность, поскольку издержки, связанные с некогерентностью, имеют место только при возникновении ситуации некогерентности. Кроме того, аппаратные методы прозрачны для программиста и пользователя [TOMA93]. Аппаратные механизмы решения проблемы когерентности принято называть протоколами когерентности кэш-памяти. Как известно, для обеспечения идентичности копий данных в кэше и основной памяти в однопроцессорных системах применяется одна из двух стратегий: сквозная запись (write through) или обратная запись (write back). При сквозной записи новая информация одновременно заносится как в кэш, так и в основную память. При обратной записи все изменения производятся только в кэш-памяти, а обновление содержимого основной памяти происходит лишь при удалении блока из кэш-памяти путем пересылки удаляемого блока в соответствующее место основной памяти. В случае мультипроцессорной системы, когда копии совместно используемых данных могут находиться сразу в нескольких кэшах, необходимо обеспечить когерентность всех копий. Ни сквозная, ни обратная запись не предусматривают такой ситуации, и для ее разрешения используются другие приемы, а именно: запись с аннулированием (write invalidate) и запись с обновлением(write update). Последняя известна также под названием записи с трансляцией(write broadcast). При записи с аннулированием, если какой-либо процессор производит изменения в одном из блоков своей кэш-памяти, все имеющиеся копии этого блока в других локальных кэшах аннулируются, то есть помечаются как недостоверные. Для этого бит достоверности измененного блока (строки) во всех прочих кэшах устанавливается в 0. Идею записи с аннулированием иллюстрирует рис. 11.6, где показано исходное состояние системы памяти, при котороме копия переменной x имеется во всех КЭШах (рис. 11.6 а), а также состояние системы памяти после записи нового значения x в кэш память c номером 2 (рис 11.6 б). Рис. 11.6. Запись с аннулированием: а — исходное состояние; б — после изменения значения x в кэш-памяти 2 Если впоследствии другой процессор попытается прочитать данные из своей копии такого блока, произойдет кэш-промах. Следствием кэш-промаха должно быть занесение в локальную кэш-память читающего процессора корректной копии блока. Некоторые схемы когерентности позволяют получить корректную копию непосредственно из той локальной кэш-памяти, где блок подвергся модификации. Если такая возможность отсутствует, новая копия берется из основной памяти. В случае сквозной записи это может быть сделано сразу же, а при использовании обратной записи модифицированный блок предварительно должен быть переписан в основную память. Запись с обновлением предполагает, что любая запись в локальный кэш немедленно дублируется и во всех остальных кэшах, содержащих копию измененного блока (немедленное обновление блока в основной памяти не является обязательным). Запись с обновлением иллюстрирует рис. 11.7. Рис. 11.7. Запись с обновлением: а — исходное состояние; б — после изменения значения x в кэш-памяти 2 Стратегия записи с обновлением требует широковещательной передачи новых данных по сети межсоединений, что возможно не при любой топологии сети. В общем случае для поддержания когерентности в мультипроцессорных системах имеются следующие возможности: l совместно используемая кэш-память; l некэшируемые данные; l широковещательная запись; l протоколы наблюдения; l протоколы на основе справочника. Совместно используемая кэш-память. Первое и наиболее простое решение — вообще отказаться от локальных кэшей и все обращения к памяти адресовать к одной общей кэш-памяти, связанной со всеми процессорами посредством какой-либо коммуникационной сети. Хотя данный прием обеспечивает когерентность копий данных и прозрачен для пользователя, количество конфликтов по доступу к памяти он не снижает, поскольку возможно одновременное обращение нескольких процессоров к одним и тем же данным в общей кэш-памяти. Кроме того, использование разделяемой кэш-памяти нарушает важнейшее условие высокой производительности, согласно которому процессор и кэш-память должны располагаться как можно ближе друг к другу. Положение осложняется и тем, что каждый доступ к кэшу связан с обращением к арбитру, который определяет, какой из процессоров получит доступ к кэш-памяти. Тем не менее, общая задержка обращения к памяти в целом уменьшается. Некэшируемые данные. Проблема когерентности связана с теми данными, которые в ходе выполнения программы могут быть изменены. Одно из возможных решений — это запрет кэширования таких данных. Технически, запрет на кэширование отдельных байтов и слов достаточно трудно реализуем. Несколько проще сделать некэшируемым определенный блок данных. При обращении процессора к такому блоку возникает ситуация кэш-промаха, производится доступ к основной памяти, но копия блока в кэш не заносится. Для реализации подобного приема каждому блоку в основной памяти должен быть придан признак, указывающий, является ли блок кэшируемым или нет. Если кэш-система состоит из раздельных кэшей команд и данных, сказанное относится, главным образом, к кэш-памяти данных, поскольку современные подходы к программированию не рекомендуют модификацию команд программы. Следовательно, по отношению к информации в кэше команд применяется только операция чтения, что не влечет проблемы когерентности. В отношении того, какие данные не должны кэшироваться, имеется несколько подходов. В первом варианте запрещается занесение в кэш лишь той части совместно используемых данных, которая используется для управления критическими секциями программы, т.е. теми частями программы, где процессоры могут изменять совместно используемые данные. Принятие решения о том, какие данные могут кэшироваться, а какие — нет, возлагается на программиста, что делает этот способ непрозрачным для пользователя. Во втором варианте запрещается кэширование всех совместно используемых данных, которые в процессе выполнения программы могут быть изменены. Естественно, что для доступа к таким данным приходится обращаться к медленной основной памяти и производительность процессора падает. На первый взгляд, в варианте, где запрещается кэширование только управляющей информации, производительность процессора будет выше, однако, прежде чем сделать такой вывод, нужно учесть одно обстоятельство. Дело в том, что для сохранения согласованности данных, модифицируемых процессором в ходе выполнения критической секции программы, строки с копиями этих данных в кэш-памяти при выходе из критической секции нужно аннулировать. Данная операция носит название очистки кэш-памяти (cache flush). Очистка необходима для того, чтобы к моменту, очередного входа в критическую секцию в кэш-памяти не осталось «устаревших» данных. Регулярная очистка кэша при каждом выходе из критической секции снижает производительность процессора за счет увеличения времени, нужного для восстановления копий в кэш-памяти. Ситуацию можно несколько улучшить, если вместо очистки всей кэш-памяти помечать те блоки, к которым при выполнении критической секции было обращение, тогда при выходе из критической секции достаточно очищать только эти помеченные блоки. Широковещательная запись. При широковещательной записи каждый запрос на запись в конкретную кэш-память направляется также и всем остальным кэшам системы. Это заставляет контроллеры кэшей проверить, нет ли там копии изменяемого блока. Если такая копия найдена, то она аннулируется или обновляется, в зависимости от применяемой схемы (с аннулированием или обновлением). Метод широковещательной записи связан с дополнительными групповыми операциями с памятью (транзакциями), поэтому он реализован лишь в больших вычислительных системах.
|