Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Предположим, анализ производительности вашего приложения выявил, что существенная часть процессорного времени тратится в некой вычислительной функции, и более того, эта функция многократно вызывается с одними и теми же параметрами — выполняя одинаковые вычисления вновь и вновь. Напрашивается простая оптимизация — кэш из одной записи, в котором бы хранились исходные данные и результат последнего вычисления.
void CalculatingThread(void)
{
L4_ThreadId_t tid;
L4_Msg_t msg;
L4_MsgTag_t tag;
BOOL result;
int n;
while( true )
{
tag = L4_Wait( &tid ); // ждём сообщение
L4_Store( tag, &msg );
n = (int) L4_Get( &msg, 0 ); // берём первый аргумент сообщения
result = IsPrime( n ); // вызываем потоко-небезопасную функцию
L4_Clear(&msg);
L4_Append( &msg, (L4_Word_t) n ); // формируем ответ
L4_Load( &msg );
L4_Send( tid ); // посылаем ответ
}
}
Я вот сомневаюсь вообще в основной мысли Чена: давайте избавимся от системных вызовов синхронизации с целью достижения производительности, но при этом заменим их на интенсивный поток Interlocked-операций.Вы неверно его поняли. Его основная мысль — «посмотрите, какая интересная штука, какие интересные у неё характеристики; может быть, вам однажды пригодится»
Вот в данном случае: а что мешает использовать Thread Local Storage для того, чтобы сделать по кэшу на каждую нить?Чен на этот вопрос ответил сам: «Если вам достаточно одного кэша на поток, то TLS подходит отлично. А вдруг вам нужен отдельный кэш в каждом объекте? И это не высосанная из пальца ситуация. Например, „пусть у каждой транзакции будет свой собственный кэш SID, потому что её права скорее всего не изменяются; но у различных транзакций, скорее всего, будут различные SID-ы“.»
Если предусматривать возможность обработки объекта разными нитями, то там на каждый чих придётся делать синхронизацию. Это всю производительность убьёт.Речь, собственно, об обратном: одна нить (например, STA-апартмент) обрабатывает несколько объектов, и у каждого объекта свой собственный кэш.
Тут гораздо выгоднее обрабатывать определённые объёмы объектов одной нитью, и делить такую обработку через пул нитей и очереди задач.
<основная функция> ---> <сложный обработчик> ---> <ещё один уровень обработчика>
+---<-----------<------------<-----------<---------<-----------+
| ^
<основная функция> ---> <сложный обработчик> ---> <ещё один уровень обработчика> --+
nLast и fLastPrime запросто могут оказаться рассогласованными.// WARNING! IF YOU USE THIS CODE YOU ARE AN IDIOT - READ THE TEXT ABOVEА в тексте поста — подобное разъяснение, что его код — иллюстрация объясняемых принципов, а не пример для подражания. И что если бы он постил в блоге код «промышленного качества», то никто бы не продрался сквозь второстепенную шелуху к той сути, ради которой код постится.
Беззамочные алгоритмы: ненастойчивый кэш