Комментарии 30
MESI — это базовый протокол синхронизации кэшей, ранее использовавшийся в микроархитектурой с общей шиной (FSB). Сейчас, с переходом на QPI используют модифицированный протокол MESIF. Программистам, даже тем, кто оптимизирует для микроархитектуры, подробности реализации алгоритма будут в основном скучны — мне так кажется. Но если действительно интересно, то можно почитать интересную статейку, где сравниваются несколько протоколов, актуальных на данный момент.
Может быть с точки зрения программиста разницу между MESI и MESIF понимать не нужно. Но не рассказав про тот же MESI крайне сложно объяснить, в чем проблема с false sharing.
Если честно, я не согласен, что для понимания проблемы false sharing необходимо объяснять весь принцип работы протокола когерентности кэша. Я просто упомянул следствие его работы — перевод кэш-линии в состояние невалидной при модификации ее элементов. И далее, заметил, что для восстановления идентичности данных в кэшах ядер (синхронизация) требуется некоторое время (которое теряет наша программа).
Понять прицип работы протокола можно, пойдя по ссылкам. А загружать свой пост такими подробностями было бы жестоко по отношению к читателям. Меня и так слегка пинают за излишнюю избыточность и сложность моих обзоров. Поэтому я попробовал соблюсти разумный баланс.
Понять прицип работы протокола можно, пойдя по ссылкам. А загружать свой пост такими подробностями было бы жестоко по отношению к читателям. Меня и так слегка пинают за излишнюю избыточность и сложность моих обзоров. Поэтому я попробовал соблюсти разумный баланс.
Конкурентная read-read производительность при обращение к одной линейки же ок.
В статье это не указано явно, и может создаться неверное впечатление, что false sharing это всегда плохо, и нужно его лечить.
Кстати, меня всегда волновал вопрос актуальности оптимизации операций с кэшем в современных не-RT осях. Там же запущенно 100500 процессов в фоне, которые вполне вероятно обращаются к данным в памяти, и мусорят в L1/L2 кэше и портят производительность оптимизированным программам!
В статье это не указано явно, и может создаться неверное впечатление, что false sharing это всегда плохо, и нужно его лечить.
Кстати, меня всегда волновал вопрос актуальности оптимизации операций с кэшем в современных не-RT осях. Там же запущенно 100500 процессов в фоне, которые вполне вероятно обращаются к данным в памяти, и мусорят в L1/L2 кэше и портят производительность оптимизированным программам!
Обращение к одной кэш-линии по чтению всеми потоками с высокой производительностью — это, собственно, главное назначение и сама суть системы организации памяти, поэтому я даже не догадался об этом написать. Но замечание верное.
Что касается 100500 потоков в системе — нас это не должно сильно волновать, так как эти потоки принадлежат разным процессам, которым операционная система предоставляет доступ, переключая контекст. Это все-рано приводит к вытеснению данных из кэшей, если все процессы активно работают и используют память. С этим ничего не поделать. Другое дело — одни процесс, наша программа, которую мы оптимизируем. Мы можем иметь сколько угодно потоков (число ограничено лимитами ресурсов ОС), но они должны «спать», а активными, т.к. готовыми к исполнению, должно быть количество потоков, равное числу логических процессоров в системе — тогда баланс будет соблюден. Дальше уже требуется работать над оптимизацией расположения данных в кэш-памяти.
Что касается 100500 потоков в системе — нас это не должно сильно волновать, так как эти потоки принадлежат разным процессам, которым операционная система предоставляет доступ, переключая контекст. Это все-рано приводит к вытеснению данных из кэшей, если все процессы активно работают и используют память. С этим ничего не поделать. Другое дело — одни процесс, наша программа, которую мы оптимизируем. Мы можем иметь сколько угодно потоков (число ограничено лимитами ресурсов ОС), но они должны «спать», а активными, т.к. готовыми к исполнению, должно быть количество потоков, равное числу логических процессоров в системе — тогда баланс будет соблюден. Дальше уже требуется работать над оптимизацией расположения данных в кэш-памяти.
> Это все-рано приводит к вытеснению данных из кэшей, если все процессы активно работают и используют память
Дык о том и речь, что может получиться так что оптимизация по кэшу срабатывать будет крайне редко, ибо есть другие процессы, которые портят кэш. Вот поэтому и возникает вопрос, насколько это эффективно в реальных, а не эталонных, системах.
Правда, с другой стороны само переключение контекстов занимает сотни, а то и тысячи тактов, и можно, наверно, забить на потери при cache-miss'ах при возобновлении потока.
Дык о том и речь, что может получиться так что оптимизация по кэшу срабатывать будет крайне редко, ибо есть другие процессы, которые портят кэш. Вот поэтому и возникает вопрос, насколько это эффективно в реальных, а не эталонных, системах.
Правда, с другой стороны само переключение контекстов занимает сотни, а то и тысячи тактов, и можно, наверно, забить на потери при cache-miss'ах при возобновлении потока.
В случае системы, в которой работают много процессов, нагружающих память, оптимизация будет работать в рамках кванта времени, выделенного на наш процесс. Но это тоже важно, так как в относительных величинах программа бутет все-рано работать быстрее.
Вы правы, переключение контекстов занимает больше тысячи тактов, и забить можно на потери при cache-miss'aх которые возникнут при заполнении кэшей данными заново, а вот на остальные cache-miss'ы забивать не надо )
Вы правы, переключение контекстов занимает больше тысячи тактов, и забить можно на потери при cache-miss'aх которые возникнут при заполнении кэшей данными заново, а вот на остальные cache-miss'ы забивать не надо )
Проблема, которую вы описываете, играет существенную роль для систем с общим LLC, на которых на разных ядрах исполняются RT и GPOS, с или без виртуализации. Если цикл для RT системы порядка сотен микросекунд с толерантностью в десятки микросекунд, вытеснения данных RT процесса из LLC действительно будет критичным.
В ARM есть Cache lock down, которые это решает, в x86 (пока) нет.
В ARM есть Cache lock down, которые это решает, в x86 (пока) нет.
Так на сколько, в результате, увеличивается скорость?
Подскажите пожалуйста, кто ответственен за распределение потоков по ядрам на процессоре с HyperThreading (для уменьшения борьбы за L1/L2) — программист или ОС?
По-умолчанию, ОС сама назначает исполнение потоков в логических процессорах. Но в случаях, когда это оправдано с точки зрения производительности, программист может сам привязывать потоки к процессорам с помощью механизма аффинитизации — наиболее часто применяется для NUMA-платформ, где бывает важно данные локализовать в «своей» памяти процессора. Привязка к процессорам для улучшения работы кэшей неэффективна.
Вот есть статейка про HT, как его использовать эффективно.
Вот есть статейка про HT, как его использовать эффективно.
> P.S. Попробуйте мой примерчик, и расскажите, на сколько процентов увеличилось быстродействие теста на вашей платформе.
Ну, для этого полную готовую программу выложить было бы неплохо, наверное? Хотя бы исходник, про бинарник уж молчу.
Ну, для этого полную готовую программу выложить было бы неплохо, наверное? Хотя бы исходник, про бинарник уж молчу.
Да, было бы неплохо…
«Так вы что, и конфеты за меня есть будете?!.. — АГА!!!»
[Вовка в Тридевятом царстве]
«Так вы что, и конфеты за меня есть будете?!.. — АГА!!!»
[Вовка в Тридевятом царстве]
Т.к. начинаю понемногу баловаться с OpenCL, то написал простенький kernel (в OpenCL так называется код, который выполняется на устройстве в несколько потоков) для заполнения матрицы размером 16*16. Запустил 5 млн итераций. Процессор Intel Core i5 650 (3.2 GHz), OS Ubuntu 10.04 x64
1) Кол-во потоков: 256, время выполнения: 69 сек
2) Кол-во потоков: 16, время выполнения: 63 сек
1) Кол-во потоков: 256, время выполнения: 69 сек
2) Кол-во потоков: 16, время выполнения: 63 сек
Работаю с SoC имеющей 2 ядра с кешем, но с отсутствующим механизмом когерентности кешей. Основные правила:
1. Не использовать глобальные переменные
2. Если все же без глобальных переменных не обойтись, то выравнить их по длине кеш линии (16 байт в моем случае).
3. Попытаться сгруппировать глобальные переменные в структуры, чтобы избежать траты памяти из-за padding байт.
3. Использовать некешируемый доступ к этой переменной/структуре.
3. Попытаться сгруппировать глобальные переменные в структуры, чтобы избежать траты памяти из-за padding байт.
3. Использовать некешируемый доступ к этой переменной/структуре.
Кстати, о масштабируемых аллокаторах TBB теперь можно почитать в HTML версии Справочного руководства Intel® TBB на нашем опен-сорс сайте: threadingbuildingblocks.org/docs/help/
осталость выяснить, сохранится ли выигрыш в производительности, если пользователь запустит эту программу под Atom, AMD Fusion или вообще под виртуальной машиной
перестали работать картинки
The server encountered an internal error or misconfiguration and was unable to complete your request.
The server encountered an internal error or misconfiguration and was unable to complete your request.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Делиться не всегда полезно: оптимизируем работу с кэш-памятью