Ложный обмен
В информатике . ложное совместное использование — это модель использования, снижающая производительность, которая может возникнуть в системах с распределенными, когерентными кэшами размером с наименьший блок ресурсов, управляемый механизмом кэширования Когда участник системы пытается периодически получить доступ к данным, которые не изменяются другой стороной, но эти данные используют общий блок кэша с изменяемыми данными , протокол кэширования может заставить первого участника перезагрузить весь блок кэша, несмотря на отсутствие логическая необходимость. [1] Система кэширования не знает о деятельности внутри этого блока и заставляет первого участника нести накладные расходы системы кэширования, необходимые для истинного совместного доступа к ресурсу.
Кеши многопроцессорных процессоров [ править ]
Безусловно, наиболее распространенное использование этого термина - в современных кэшах многопроцессорных ЦП , где память кэшируется в строках небольшой степени размером в два слова (например, 64 выровненных , смежных байта ). Если два процессора работают с независимыми данными в одной и той же области адреса памяти , которые хранятся в одной строке, механизмы когерентности кэша в системе могут заставить всю строку проходить через шину или системы. соединяться при каждой записи данных, вызывая остановку памяти в дополнение к потере пропускной способности . В некоторых случаях устранение ложного совместного использования может привести к повышению производительности на порядок. [2] Ложное совместное использование является неотъемлемым артефактом протоколов автоматической синхронизации кэша и может также существовать в таких средах, как распределенные файловые системы или базы данных, но в настоящее время его распространенность ограничивается кэшами ОЗУ.
Пример [ править ]
#include <iostream>
#include <thread>
#include <new>
#include <atomic>
#include <chrono>
#include <latch>
#include <vector>
using namespace std;
using namespace chrono;
#if defined(__cpp_lib_hardware_interference_size)
// default cacheline size from runtime
constexpr size_t CL_SIZE = hardware_constructive_interference_size;
#else
// most common cacheline size otherwise
constexpr size_t CL_SIZE = 64;
#endif
int main()
{
vector<jthread> threads;
int hc = jthread::hardware_concurrency();
hc = hc <= CL_SIZE ? hc : CL_SIZE;
for( int nThreads = 1; nThreads <= hc; ++nThreads )
{
// synchronize beginning of threads coarse on kernel level
latch coarseSync( nThreads );
// fine synch via atomic in userspace
atomic_uint fineSync( nThreads );
// as much chars as would fit into a cacheline
struct alignas(CL_SIZE) { char shareds[CL_SIZE]; } cacheLine;
// sum of all threads execution times
atomic_int64_t nsSum( 0 );
for( int t = 0; t != nThreads; ++t )
threads.emplace_back(
[&]( char volatile &c )
{
coarseSync.arrive_and_wait(); // synch beginning of thread execution on kernel-level
if( fineSync.fetch_sub( 1, memory_order::relaxed ) != 1 ) // fine-synch on user-level
while( fineSync.load( memory_order::relaxed ) );
auto start = high_resolution_clock::now();
for( size_t r = 10'000'000; r--; )
c = c + 1;
nsSum += duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count();
}, ref( cacheLine.shareds[t] ) );
threads.resize( 0 ); // join all threads
cout << nThreads << ": " << (int)(nsSum / (1.0e7 * nThreads) + 0.5) << endl;
}
}
Этот код показывает эффект ложного совместного использования. Он создает увеличивающееся количество потоков от одного потока до количества физических потоков в системе. Каждый поток последовательно увеличивает один байт строки кэша, которая в целом распределяется между всеми потоками. Чем выше уровень конкуренции между потоками, тем больше времени занимает каждое приращение. Вот результаты для системы Zen4 с 16 ядрами и 32 потоками:
1: 1 2: 4 3: 6 4: 9 5: 11 6: 13 7: 15 8: 17 9: 16 10: 18 11: 21 12: 25 13: 29 14: 35 15: 39 16: 41 17: 43 18: 44 19: 48 20: 49 21: 51 22: 53 23: 58 24: 61 25: 68 26: 75 27: 79 28: 82 29: 85 30: 88 31: 91 32: 94
Как видите, в рассматриваемой системе выполнение операции увеличения строки общего кэша может занять до 100 наносекунд, что соответствует примерно 100 наносекундам. 420 тактов на этом процессоре.
Смягчение [ править ]
Существуют способы смягчить последствия ложного обмена. Например, ложное совместное использование кэшей ЦП можно предотвратить путем изменения порядка переменных или добавления заполнения (неиспользуемых байтов) между переменными. Однако некоторые из этих изменений в программе могут увеличить размер объектов, что приведет к более интенсивному использованию памяти. [2] Преобразования данных во время компиляции также могут уменьшить ложное совместное использование. [3] Однако некоторые из этих преобразований не всегда могут быть разрешены. Например, проект стандарта языка программирования C++ C++23 требует, чтобы члены данных располагались так, чтобы последующие члены имели более высокие адреса. [4]
Существуют инструменты для обнаружения ложного обмена. [5] [6] Существуют также системы, которые обнаруживают и устраняют ложное разделение при выполнении программ. Однако эти системы несут некоторые накладные расходы на выполнение. [7] [8]
Ссылки [ править ]
- ^ Паттерсон, Дэвид (2012). Организация и проектирование компьютера: аппаратно-программный интерфейс . Уолтем, Массачусетс: Морган Кауфманн. п. 537. ИСБН 978-0-12-374750-1 . OCLC 746618653 .
- ↑ Перейти обратно: Перейти обратно: а б Болоски, Уильям Дж.; Скотт, Майкл Л. (22 сентября 1993 г.). «Ложное разделение и его влияние на производительность разделяемой памяти» . Sedms'93: Системы USENIX об опыте USENIX в работе с распределенными и многопроцессорными системами . 4 . Проверено 11 июля 2021 г.
- ^ Джеремиассен, Тор Э.; Эггерс, Сьюзен Дж. (1995). «Уменьшение ложного совместного использования на мультипроцессорах с общей памятью посредством преобразования данных во время компиляции» . Уведомления ACM SIGPLAN . 30 (8). Ассоциация вычислительной техники (ACM): 179–188. дои : 10.1145/209937.209955 . ISSN 0362-1340 .
- ^ «Рабочий проект стандарта языка программирования C++ [класс]» . угорь.есть . Проверено 11 июля 2021 г.
- ^ "perf-c2c(1)" . Страница руководства по Linux . 01.09.2016 . Проверено 8 августа 2021 г.
- ^ Чабби, Милинд; Вэнь, Шаша; Лю, Сюй (10 февраля 2018 г.). «Оперативное обнаружение ложных сообщений Featherlight». Материалы 23-го симпозиума ACM SIGPLAN по принципам и практике параллельного программирования . Нью-Йорк, штат Нью-Йорк, США: ACM. стр. 152–167. дои : 10.1145/3178487.3178499 . ISBN 9781450349826 .
- ^ Нанавати, Михир; Копье, Марк; Тейлор, Натан; Раджагопалан, Шрирам; Мейер, Датч Т.; Айелло, Уильям; Уорфилд, Эндрю (2013). «Чья это вообще строка кэша?». Материалы 8-й Европейской конференции ACM по компьютерным системам . Нью-Йорк, Нью-Йорк, США: ACM Press. стр. 141–154. дои : 10.1145/2465351.2465366 . ISBN 9781450319942 .
- ^ Лю, Тунпин; Бергер, Эмери Д. (18 октября 2011 г.). «ШЕРИФ: точное обнаружение и автоматическое предотвращение ложного распространения информации». Уведомления ACM SIGPLAN . 46 (10). Ассоциация вычислительной техники (ACM): 3–18. дои : 10.1145/2076021.2048070 . ISSN 0362-1340 .