есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
спасибо.
желательно на визуал студии, но джисиси тоже ничего.
З.ы. результат крайне желателен сегодня — за большее время я и сам разберусь, но будет уже поздняк метаться
Здравствуйте, Molchalnik, Вы писали:
M>есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
Не знаю как там в ваших виндовсах, но в юниксах memcpy уже расписан на asm с использованием SSE. Его не надо переписывать по своему. В gcc есть builtins, врайпящие асмовые SSE инструкции.
Здравствуйте, smeeld, Вы писали:
S>Не знаю как там в ваших виндовсах, но в юниксах memcpy уже расписан на asm с использованием SSE. Его не надо переписывать по своему. В gcc есть builtins, врайпящие асмовые SSE инструкции.
как минимум, чтобы переписать memcpy на SSE, нужно потратиться на выравнивающую обвязку, что вызовет несколько джампов/goto, а каждый джамп — перезагрузка конвейера (если в предсказание конвейера не попасть)
у меня же тут клинически идеальный случай — всё уже выравнено как надо.
Здравствуйте, Molchalnik, Вы писали:
M>есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
Общая идея очень простая:
#include"emmintrin.h"void cpy(const char* src, char* dst, size_t sz) {
using M = __m128i; // __m256 для avxconst M* s = (const M*)src;
M* d = (M*)dst;
for (; sz > 0; sz -= sizeof(M) * 4) {
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
};
}
Впрочем я бы просто удостоверился, что в компиляторе используется достаточно свежая версия memcpy, которая знает про векторные инструкции, и использовал её. Потому как всё равно понадобится какая-то обвязка для такого кода. Например, нужно будет добавить код, который будет решить что делать, если avx процессором не поддерживается. И протестировать насколько вышеприведённый цикл лучше развернуть (может быть 8 итераций), и вообще может быть лучше с конца к началу копировать. И для очень длинных массивов написать ещё одну версию с non-temporal storage, чтобы кеш заполнять данными, которые всё равно туда не влезут (тут не ясно, бывает ли такой у тебя случай).
Конечно, memcpy обладает избыточностью и обрабатывает всякие невозможные тут ситуации, но даже с учётом этих затрат эта функция может быть очень хорошим выбором (до тех пор, пока профайлер не покажет, что кроме memcpy оптимизировать уже нечего)
S>>Не знаю как там в ваших виндовсах, но в юниксах memcpy уже расписан на asm с использованием SSE. Его не надо переписывать по своему. В gcc есть builtins, врайпящие асмовые SSE инструкции. M>как минимум, чтобы переписать memcpy на SSE, нужно потратиться на выравнивающую обвязку, что вызовет несколько джампов/goto, а каждый джамп — перезагрузка конвейера (если в предсказание конвейера не попасть) M>у меня же тут клинически идеальный случай — всё уже выравнено как надо.
__builtin_assume_aligned
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Molchalnik, Вы писали:
M>есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
M>спасибо.
M>желательно на визуал студии, но джисиси тоже ничего.
M>З.ы. результат крайне желателен сегодня — за большее время я и сам разберусь, но будет уже поздняк метаться
Здравствуйте, Molchalnik, Вы писали:
M>есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
в ассемблере — rep movsb. в C++ — memcpy полюс свежий компилятор
Здравствуйте, BulatZiganshin, Вы писали:
BZ>в ассемблере — rep movsb.
Это не SIMD. Кстати, movsb-это копирование побайтно, в наборе имеются инструкции копирования пословно, movsw, movsd, поэтому правильней сказать rep movsX.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>нет, используй именно rep movsb
Буду использовать пакетный мовинг через AVX или XMM регистры. Так получается быстрее всего, измерял. rep movsX вообще уже из разяда устаревающего хлама.
Здравствуйте, smeeld, Вы писали:
S>Буду использовать пакетный мовинг через AVX или XMM регистры. Так получается быстрее всего, измерял. rep movsX вообще уже из разяда устаревающего хлама.
ясно. советую тебе читать intel/fog optimization manuals, чтобы в следующий раз не попасть впросак
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, smeeld, Вы писали:
S>>Буду использовать пакетный мовинг через AVX или XMM регистры. Так получается быстрее всего, измерял. rep movsX вообще уже из разяда устаревающего хлама.
BZ> да и зачем вообще мерять, если у фога всё расписано??
Fog не авторитет, как и любой праздный писака-теоретик. Как они далеки от реальности можно понять заглянув в реализацию memcpy в glibc, например.
Здравствуйте, smeeld, Вы писали:
S>Fog не авторитет, как и любой праздный писака-теоретик. Как они далеки от реальности можно понять заглянув в реализацию memcpy в glibc, например.
хоть этот код и не верх совершенства, но rep movsb он использует. мог бы сразу посмотреть чтобы зря не спорить
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, Molchalnik, Вы писали:
M>>есть блок памяти, выровненный по 32 байта. длина выровнена по 1024 байта. Подскажите, пожалуйста, как не залезая в ассемблер , интринсиками или ещё чем эффективно скопировать этот блок в другой, такого же размера.
BZ>в ассемблере — rep movsb. в C++ — memcpy полюс свежий компилятор
Здравствуйте, Molchalnik, Вы писали:
BZ>>в ассемблере — rep movsb. в C++ — memcpy полюс свежий компилятор
M>почему тогда не movdqa?
Потому что (современные) процы обрабатывюет rep movsb специальным образом (a rep movqa нет), т.е. "тупое побайтовое копирование" будет быстрее чем SIMD или AVX
Beginning with processors based on Intel microarchitecture code named Ivy Bridge,
REP string operation using MOVSB and STOSB can provide both flexible and highperformance
REP string operations for software in common situations like memory
copy and set operations
BZ>>в ассемблере — rep movsb. в C++ — memcpy полюс свежий компилятор
M>почему тогда не movdqa?
movdqa будет быстрее для коротких участков (меньше 128 байт) и на на старых процессорах.
Про rep movs Агнер Фог пишет так:
The REP MOVS instruction (1) is a simple solution which is useful when optimizing for code
size rather than for speed. This instruction is implemented as microcode in the CPU. The
microcode implementation may actually use one of the other methods internally. In some
cases it is well optimized, in other cases not. Usually, the REP MOVS instruction has a large
overhead for choosing and setting up the right method. Therefore, it is not optimal for small
blocks of data.
Many modern processors have optimized the REP MOVS instruction (method 1) to use the
largest available register size and the fastest method, at least in simple cases. But there are
still cases where the REP MOVS method is slow, for example for certain misalignment cases
and false memory dependence. However, the REP MOVS instruction has the advantage that
it will probably use the largest available register size on processors in a more distant future
with registers bigger than 256 bits. As instructions with the expected future register sizes
cannot yet be coded and tested, the REP MOVS instruction is the only way we can write code
today that will take advantage of future extensions to the register size. Therefore, it may be
useful to use the REP MOVS instruction for favorable cases of large aligned memory blocks.
On many processors, REP MOVS and REP STOS can perform fast by moving 16 bytes or an
entire cache line at a time. This happens only when certain conditions are met. Depending
on the processor, the conditions for fast string instructions are, typically, that the count must
be high, both source and destination must be aligned, the direction must be forward, the
distance between source and destination must be at least the cache line size, and the
memory type for both source and destination must be either write-back or write-combining
(you can normally assume the latter condition is met).
Under these conditions, the speed is as high as you can obtain with vector register moves
or even faster on some processors.
В подтверждение можно заглянуть в официальный Intel 64 and IA-32 Architectures Optimization Reference Manual. Фича называется Enhanced REP MOVSB and STOSB operation (ERMSB). И если она поддерживается, то копирование длинных выровненных блоков будет быстрее чем реализация через SSE, AVX или что-то другое.
bnk>Потому что (современные) процы обрабатывюет rep movsb специальным образом (a rep movqa нет), т.е. "тупое побайтовое копирование" будет быстрее чем SIMD или AVX
У меня есть рацпредложение для Интел: оптимизировать и все остальные инструкции специальным образом, чтоб работали быстрее чем раньше. Глядишь и не придеться техпроцессы новые вводить..
Как много веселых ребят, и все делают велосипед...
Здравствуйте, watchmaker, Вы писали:
W>В подтверждение можно заглянуть в официальный Intel 64 and IA-32 Architectures Optimization Reference Manual. Фича называется Enhanced REP MOVSB and STOSB operation (ERMSB). И если она поддерживается, то копирование длинных выровненных блоков будет быстрее чем реализация через SSE, AVX или что-то другое.
Нифига оно не быстрее. Копирование через XMM и YMM регистры быстрее. Тестили, измеряли. Кто там лжёт, инженегеры интеля, или составители мануалов разбираться не интересно.
Здравствуйте, smeeld, Вы писали:
S>Тестили, измеряли.
Ну, измерять производительность своего конкретного кода — это здравое действие, конечно. Цифры будет ценнее чем абстрактные примеры в идеальных условиях. А то может оказаться, что в программе и не memcpy тормозит :)