Может потребоваться выравнивание по границе строки кэша, которая обычно составляет 64 байта на строку кэша, когда вы работаете с прерываниями или высокопроизводительным чтением данных, и их обязательно использовать при работе с межпроцессными сокетами.В случае межпроцессных сокетов существуют управляющие переменные, которые не могут быть распределены по нескольким строкам кэша или словам памяти DDR, иначе это приведет к тому, что L1, L2 и т. Д., Или кэши или память DDR, будут функционировать как фильтр нижних частот и отфильтровывать ваши данные прерываний.!ЭТО ПЛОХО!!!Это означает, что вы получаете странные ошибки, когда ваш алгоритм хорош и потенциально может заставить вас сходить с ума!
ОЗУ DDR почти всегда будет читать 128-битные слова (слова DDR RAM), что16 байт, поэтому переменные кольцевого буфера не должны быть распределены по нескольким словам DDR RAM.некоторые системы используют 64-битные слова DDR RAM, и технически вы можете получить 32-битное слово DDR RAM на 16-битном процессоре, но в этой ситуации можно использовать SDRAM.
Можно также просто заинтересоватьсяминимизация количества строк кэша, используемых при считывании данных в высокопроизводительном алгоритме.В моем случае я разработал самый быстрый в мире алгоритм целочисленных строк (на 40% быстрее, чем предыдущий самый быстрый алгоритм), и я работаю над оптимизацией алгоритма Грису, который является самым быстрым в мире алгоритмом с плавающей запятой.Чтобы напечатать число с плавающей запятой, вы должны вывести целое число, поэтому, чтобы оптимизировать Grisu, я реализовал одну оптимизацию, состоящую в том, что я выровнял таблицы поиска (LUT) для Grisu с выравниванием по кэш-строке в ровно 15 строк кэша, чтодовольно странно, что на самом деле это выровняли так.Это берет LUT из секции .bss (то есть статической памяти) и помещает их в стек (или кучу, но стек больше подходит).Я не проверял это, но это хорошо, и я многое узнал об этом. Самый быстрый способ загрузить значения - это загрузить их из i-кеша, а не из d-кеша.Разница в том, что i-кеш доступен только для чтения и имеет гораздо большие строки кеша, потому что он только для чтения (2 КБ было тем, что профессор однажды процитировал мне).Таким образом, вы на самом деле собираетесь снизить производительность при индексировании массива, а не загружать переменную следующим образом:
int faster_way = 12345678;
, а не медленнее:
int variables[2] = { 12345678, 123456789};
int slower_way = variables[0];
Разница в том,что int variable = 12345678
будет загружен из строк i-кеша путем смещения переменной в i-кеше с начала функции, в то время как slower_way = int[0]
будет загружен из меньших строк d-кеша с использованием намного более медленной индексации массива,Это тонкое, как я только что обнаружил, на самом деле замедляет мой и многие другие целочисленные алгоритмы.Я говорю это потому, что вы можете оптимизировать, выравнивая кеш-данные только для чтения, когда вы этого не делаете.
Как правило, в C ++ вы будете использовать функцию std::align
.Я бы посоветовал не использовать эту функцию, потому что не гарантирует оптимальной работы .Вот самый быстрый способ выравнивания по строке кэша, которая, прежде всего, я автор, и это бесстыдный плагин:
Алгоритм выравнивания памяти Kabuki Toolkit
namespace _ {
/* Aligns the given pointer to a power of two boundaries with a premade mask.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number of bits in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param mask The mask for the Least Significant bits to align. */
template <typename T = char>
inline T* AlignUp(void* pointer, intptr_t mask) {
intptr_t value = reinterpret_cast<intptr_t>(pointer);
value += (-value ) & mask;
return reinterpret_cast<T*>(value);
}
} //< namespace _
// Example calls using the faster mask technique.
enum { kSize = 256 };
char buffer[kSize + 64];
char* aligned_to_64_byte_cache_line = AlignUp<> (buffer, 63);
char16_t* aligned_to_64_byte_cache_line2 = AlignUp<char16_t> (buffer, 63);
и здесьэто более быстрая замена std :: align:
inline void* align_kabuki(size_t align, size_t size, void*& ptr,
size_t& space) noexcept {
// Begin Kabuki Toolkit Implementation
intptr_t int_ptr = reinterpret_cast<intptr_t>(ptr),
offset = (-int_ptr) & (align - 1);
if ((space -= offset) < size) {
space += offset;
return nullptr;
}
return reinterpret_cast<void*>(int_ptr + offset);
// End Kabuki Toolkit Implementation
}