Копирование памяти
От: VuDZ Россия  
Дата: 22.12.01 07:57
Оценка: 3 (1)
Subj такой — надо с максимально озможной скоростью копировать данные из одного куска памяти в другой. Подошёл бы и memcpy(), но он очень медленный. Этот вопрос скорее по асму, но может кто ответит. Вот что есть сейчас:
_asm
{
    mov esi, p2;
    mov edi, p3;
    mov ecx, copy;
    shr ecx, 3;    // devide on 4
    sub ecx, 1;    // for first step
l_p3:    movq mm0, [esi + ecx*4];
    movq [edi+ecx*4], mm0;
    dec ecx;
    jnz l_p3;
}
_asm emms; // вот без этой строки iC++ Compiler ругается сильно :(


Дапоможите, хто чем может (желательно не теорией)
ЗЫ prefetcht уже использовался — с ним только хуже
mm0 + mm1 + mm2 + mm3 одновременно то же использовал.
сейчвс этот код на 36% быстрее. чем memcpy() на гиговом Атлоне, но может можно его ещё ускорить :shuffle: ? А то :crash:, только мне по голове

Thanx for your attantion ;)

11.02.03 20:55: Перенесено модератором из 'C/C++' — ПК
Re: Копирование памяти
От: Alex Fedotov США  
Дата: 22.12.01 09:55
Оценка:
Здравствуйте VuDZ, Вы писали:

VDZ>Subj такой — надо с максимально озможной скоростью копировать данные из одного куска памяти в другой. Подошёл бы и memcpy(), но он очень медленный. Этот вопрос скорее по асму, но может кто ответит. Вот что есть сейчас:

VDZ>
VDZ>_asm
VDZ>{
VDZ>    mov esi, p2;
VDZ>    mov edi, p3;
VDZ>    mov ecx, copy;
VDZ>    shr ecx, 3;    // devide on 4
VDZ>    sub ecx, 1;    // for first step
VDZ>l_p3:    movq mm0, [esi + ecx*4];
VDZ>    movq [edi+ecx*4], mm0;
VDZ>    dec ecx;
VDZ>    jnz l_p3;
VDZ>}
VDZ>_asm emms; // вот без этой строки iC++ Compiler ругается сильно :(
VDZ>


VDZ>Дапоможите, хто чем может (желательно не теорией)

VDZ>ЗЫ prefetcht уже использовался — с ним только хуже
VDZ>mm0 + mm1 + mm2 + mm3 одновременно то же использовал.
VDZ>сейчвс этот код на 36% быстрее. чем memcpy() на гиговом Атлоне, но может можно его ещё ускорить ? А то , только мне по голове

Аргументы выравнены на 8 байт?
Какой типичный размер копируемых данных?
-- Alex Fedotov
Re[2]: Копирование памяти
От: VuDZ Россия  
Дата: 22.12.01 11:04
Оценка:
AF>Аргументы выравнены на 8 байт?
AF>Какой типичный размер копируемых данных?

выравнивание 16ти байтовое
размер — от 64Kb до 16Mb, все размеры кратны 16.
Я думаю, что вообще-то это максимум, но может будут какие идеи?
Re: Копирование памяти
От: IT Россия linq2db.com
Дата: 22.12.01 14:39
Оценка:
Здравствуйте VuDZ, Вы писали:

VDZ>Subj такой — надо с максимально озможной скоростью копировать данные из одного куска памяти в другой. Подошёл бы и memcpy(), но он очень медленный.


А ты не пробовал #pragma intrinsic(memcpy)? Может не придётся и с асмом заморачиваться.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Копирование памяти
От: VuDZ Россия  
Дата: 22.12.01 15:58
Оценка:
Здравствуйте IT, Вы писали:

IT>Здравствуйте VuDZ, Вы писали:


VDZ>>Subj такой — надо с максимально озможной скоростью копировать данные из одного куска памяти в другой. Подошёл бы и memcpy(), но он очень медленный.


IT>А ты не пробовал #pragma intrinsic(memcpy)? Может не придётся и с асмом заморачиваться.

Это увеличивает необходимое время, незначительно, но всё таки (781 vs 761 == 2.5%)... Видимо, ММХ единственный вариант ускорить копирование...
Re[3]: Копирование памяти
От: Alex Fedotov США  
Дата: 23.12.01 06:19
Оценка: 8 (2)
Здравствуйте VuDZ, Вы писали:

AF>>Аргументы выравнены на 8 байт?

AF>>Какой типичный размер копируемых данных?

VDZ>выравнивание 16ти байтовое

VDZ>размер — от 64Kb до 16Mb, все размеры кратны 16.
VDZ>Я думаю, что вообще-то это максимум, но может будут какие идеи?

В свое время я экспериментировал с этим на PIII и для больших блоков (> 1MB) несколько помог movntq, не знаю, есть ли аналоги на Athlon.
-- Alex Fedotov
Re[2]: Копирование памяти
От: Alex Fedotov США  
Дата: 23.12.01 06:30
Оценка:
Здравствуйте IT, Вы писали:

VDZ>>Subj такой — надо с максимально озможной скоростью копировать данные из одного куска памяти в другой. Подошёл бы и memcpy(), но он очень медленный.


IT>А ты не пробовал #pragma intrinsic(memcpy)? Может не придётся и с асмом заморачиваться.


С этой прагмой memcpy разворачивается в тупой rep movsd, что совершенно неприемлемо, если данные невыравнены. Библиотечная версия пытается делать выравненную запись, в то время как на PII и PIII гораздо важнее иметь выравненное чтение (за AMD не скажу — точно не знаю, но думаю, что write combining и у них есть).

В свое время я столкнулся с ситуацией, когда нужно было делать примерно следующее.

WORD buffer1[N]; // DWORD-aligned
WORD buffer2[N]; // DWORD-aligned as well

memcpy(buffer2 + 1, buffer1, (N — 1) * sizeof(WORD));

Короче, you got the idea. Здесь получалось, что либо чтение будет DWORD-aligned, либо запись — данные так устроены. memcpy выбирала запись и проигрывала — когда я сделал свою версию, которая делала DWORD-aligned чтение, а писала как получится, она оказалась в среднем на 30% быстрее (PIII).

Но к проблеме VuDZ это отношения не имеет — он говорит, у него данные выравнены.
-- Alex Fedotov
Re[4]: Копирование памяти
От: VuDZ Россия  
Дата: 23.12.01 08:18
Оценка: 27 (5)
Здравствуйте Alex Fedotov

AF>В свое время я экспериментировал с этим на PIII и для больших блоков (> 1MB) несколько помог movntq, не знаю, есть ли аналоги на Athlon.


1. movntq — MMX команда
2. большое спасибо — скорость выросла очень сильно
_asm
{
mov esi, p1;
mov edi, p4;
mov ecx, copy;
shr ecx, 3; // devide on 8
l_p3:
movq mm0, [esi + ecx * 8]; // loading to cashe first32 bytes
movq mm1, [esi + ecx * 8 — 8];
movq mm2, [esi + ecx * 8 — 16];
movq mm3, [esi + ecx * 8 — 24];
prefetcht0 [esi + ecx * 8 — 33]; // preload next 32 bytes (prev)
movntq [edi + ecx * 8], mm0;
movntq [edi + ecx * 8 — 8], mm1;
movntq [edi + ecx * 8 — 16], mm2;
movntq [edi + ecx * 8 — 32], mm3;
sub ecx, 4;
jnz l_p3;
}

почти в два раза быстрее, чем было :)
memcpy() отдыхает:
Copying 4194304 bytes for 1000 times
press any key to begin
single movq instruction for 12769 ms
single movntq instruction for 8261 ms
qwad movntq instructions for 7752 ms
qwad movq instructions for 12317 ms
rep movsd instruction for 17555 ms
ЗЫ prefetcht0 имеет смысл использовать только тогда, когда копирутся за один проход 32 байта — это даёт прирост порядка 10%
Re: Копирование памяти
От: VuDZ Россия  
Дата: 23.12.01 10:02
Оценка:
В общем вроде бы то, чего я добился — максимум-
Copying 16777216 bytes for 100 times
press any key to begin
========= test ========         read-write            read           write
single movq instruction            6139 ms         2664 ms         3975 ms
                                   261Mb/s         601Mb/s         403Mb/s

single movntq instruction          3986 ms                          972 ms
                                   401Mb/s                        1646Mb/s

qwad movq instructions             5658 ms         1812 ms         4006 ms
                                   283Mb/s         883Mb/s         399Mb/s

qwad movntq instructions           3715 ms                          982 ms
                                   431Mb/s                        1629Mb/s


тут можно посмотреть исходник
Re: Копирование памяти
От: Eugene_White Россия  
Дата: 23.12.01 11:32
Оценка: 1 (1)
Здравствуйте VuDZ.

Есть более рациональный способ. Через "rep movs(b,w,d)" — в зависимости от требуемого.
Пример.

mov ds:esi, p2 // откуда
mov es:edi, p3 // куда
mov ecx, size // сколько байт
rep movsb

если movsw, то size/2
если movsd — size/4
etc...

и больше ничего — скорость максимальная.

EW
Re[2]: Копирование памяти
От: VuDZ Россия  
Дата: 23.12.01 12:24
Оценка:
Здравствуйте Eugene_White, Вы писали:

EW>Здравствуйте VuDZ.


EW> Есть более рациональный способ. Через "rep movs(b,w,d)" — в зависимости от требуемого.

EW>Пример.
EW>
EW> mov ds:esi, p2 // откуда
EW> mov es:edi, p3 // куда
EW> mov ecx, size // сколько байт
EW> rep movsb

EW>если movsw, то size/2

EW>если movsd — size/4
EW>etc...

EW>и больше ничего — скорость максимальная.


К сожалению, вы не правы — этот вариант в полтора раза медленне самого медленного MMX варианта... а до лидера (4 x movntq) надо быть более чем в три раза быстрее...
этот вариант я отбросил сразу же, после анализа — чем больше копируется в регистры за один раз, тем больше производительность...
Re[5]: Копирование памяти
От: Alex Fedotov США  
Дата: 23.12.01 20:14
Оценка:
Здравствуйте VuDZ, Вы писали:

VDZ>Здравствуйте Alex Fedotov


AF>>В свое время я экспериментировал с этим на PIII и для больших блоков (> 1MB) несколько помог movntq, не знаю, есть ли аналоги на Athlon.


VDZ>1. movntq — MMX команда


Она оперирует с операндом в MMX-регистре, но появилась-то она как часть SSE. Вот, кстати, и ссылочка на эту тему нашлась:
http://developer.intel.com/software/idap/media/pdf/copy.pdf
-- Alex Fedotov
Re[6]: Копирование памяти
От: VuDZ Россия  
Дата: 24.12.01 02:19
Оценка:
Здравствуйте Alex Fedotov, Вы писали:


VDZ>>1. movntq — MMX команда


AF>Она оперирует с операндом в MMX-регистре, но появилась-то она как часть SSE. Вот, кстати, и ссылочка на эту тему нашлась:

AF>http://developer.intel.com/software/idap/media/pdf/copy.pdf

За ссылку спасибо.
Ьжет вы перепутали movntq с movntps — это SSЕ команда и котрая делает почти то же самое: запись 128 битного регистра минуя кеш.
А то у меня на атлоне нет поддерки SSE
Re[7]: Копирование памяти
От: Alex Fedotov США  
Дата: 24.12.01 05:25
Оценка:
Здравствуйте VuDZ, Вы писали:

VDZ>Здравствуйте Alex Fedotov, Вы писали:


VDZ>>>1. movntq — MMX команда


AF>>Она оперирует с операндом в MMX-регистре, но появилась-то она как часть SSE. Вот, кстати, и ссылочка на эту тему нашлась:

AF>>http://developer.intel.com/software/idap/media/pdf/copy.pdf

VDZ>За ссылку спасибо.

VDZ>Ьжет вы перепутали movntq с movntps — это SSЕ команда и котрая делает почти то же самое: запись 128 битного регистра минуя кеш.

Чтобы завершить этот (бесполезный) спор надо попробовать выполнить movntq на PII — ставлю на STATUS_ILLEGAL_INSTRUCTION. Я смогу это сделать завтра утром (т.е. сегодня вечером по Московскому времени), если кому-то не влом сделать это раньше, буду ему премного благодарен.

VDZ>А то у меня на атлоне нет поддерки SSE


Набор команд процессоров от AMD порой пересекается с интеловскими весьма причудливым образом.
-- Alex Fedotov
Re[8]: Копирование памяти
От: VuDZ Россия  
Дата: 24.12.01 06:19
Оценка:
AF>Чтобы завершить этот (бесполезный) спор надо попробовать выполнить movntq на PII — ставлю на STATUS_ILLEGAL_INSTRUCTION. Я смогу это сделать завтра утром (т.е. сегодня вечером по Московскому времени), если кому-то не влом сделать это раньше, буду ему премного благодарен.
Да вроде спора и не было...

VDZ>>А то у меня на атлоне нет поддерки SSE :))


AF>Набор команд процессоров от AMD порой пересекается с интеловскими весьма причудливым образом.

Я тут закачал одну утилитку от AMD, вот результаты
features = 000013f7
CPU supports CPUID: y
CPU supports CPUID STD: y
CPU supports CPUID EXT: y
CPU supports TSC: y
CPU supports CMOV: y
CPU supports MMX: y
CPU supports 3DNOW: y
CPU supports 3DNOW_EXT: y
CPU supports AMD-K6-MTRR: n
CPU supports P6-MTRR: y
CPU supports SSE MMX: y
CPU supports SSE FPU: n

и как бы это объяснить?..
странно это...
а на втором пне или целероне не пошло, недопустимая операция однако...
следовательно, k7 поддерживают SSE MMX команды, но в них нет SSE FPU, который появился в palamino...
Re[3]: Копирование памяти
От: Eugene_White Россия  
Дата: 24.12.01 12:45
Оценка:
Здравствуйте VuDZ, Вы писали:

VDZ>К сожалению, вы не правы — этот вариант в полтора раза медленне самого медленного MMX варианта... а до лидера (4 x movntq) надо быть более чем в три раза быстрее...

VDZ>этот вариант я отбросил сразу же, после анализа — чем больше копируется в регистры за один раз, тем больше производительность...

Может я и погорячился насчет максимальной скорости (извините :), но давайте рассмотрим Ваш первоначальный вариант. Насколько видно, очень много тратится впустую тактов выражениями

l_p3: movq mm0, [esi + ecx*4]
movq [edi+ecx*4], mm0
dec ecx
jnz l_p3,

т.е. перенос через переменные. Это замедляет работу. Неужели не лучше будет доверить это процессору командой "rep" (почему ее Алекс Федотов "давит")? Она сама уменьшает есх и "лупит" — отсюда быстродействие. И где гарантия, что новых процессорах нет команд типа movsq и выше??
Спасибо.

EW
Re[4]: Копирование памяти
От: Alex Fedotov США  
Дата: 24.12.01 12:55
Оценка:
Здравствуйте Eugene_White, Вы писали:

VDZ>>К сожалению, вы не правы — этот вариант в полтора раза медленне самого медленного MMX варианта... а до лидера (4 x movntq) надо быть более чем в три раза быстрее...

VDZ>>этот вариант я отбросил сразу же, после анализа — чем больше копируется в регистры за один раз, тем больше производительность...

EW> Может я и погорячился насчет максимальной скорости (извините , но давайте рассмотрим Ваш первоначальный вариант. Насколько видно, очень много тратится впустую тактов выражениями

EW>
EW>l_p3: movq mm0, [esi + ecx*4]
EW> movq [edi+ecx*4], mm0
EW> dec ecx
EW> jnz l_p3,

EW>т.е. перенос через переменные. Это замедляет работу. Неужели не лучше будет доверить это процессору командой "rep" (почему ее Алекс Федотов "давит")? Она сама уменьшает есх и "лупит" — отсюда быстродействие. И где гарантия, что новых процессорах нет команд типа movsq и выше??


Дело в том, что при копировании больших объемов данных (больше, чем помещается в кэш первого уровня), количество тактов в инструкциях не имеет практически никакого значения. Решающее значение имеют операции, выполняемые на внешней шине процессора, так как она имеет ограниченную пропускную способность. В справочниках приводится количество тактов на инструкцию, когда операнды находятся в кэше первого уровня, когда же операнд приходится читать из памяти, это занимает на порядок больше времени.

Обычные команды записи, и movs в том числе, сначала считывают всю строку (32 байта) в кэш, а затем производят запись в кэш и (чуть позже) в память. Это разумно для работы с обычными переменными, но не для записи огромных массивов памяти, которые все равно не поместятся в кэш. Команды movntq, movntps и movntus не выполняют это бесполезное чтение и не засоряют кэши всех уровней ненужными данными, отсюда и заметный прирост в производительности.
-- Alex Fedotov
Re[4]: Копирование памяти
От: VuDZ Россия  
Дата: 24.12.01 13:30
Оценка:
Здравствуйте Eugene_White, Вы писали:

EW> Может я и погорячился насчет максимальной скорости (извините , но давайте рассмотрим Ваш первоначальный вариант. Насколько видно, очень много тратится впустую тактов выражениями

EW>
EW>l_p3: movq mm0, [esi + ecx*4]
EW> movq [edi+ecx*4], mm0
EW> dec ecx
EW> jnz l_p3,

EW>т.е. перенос через переменные. Это замедляет работу. Неужели не лучше будет доверить это процессору командой "rep" (почему ее Алекс Федотов "давит")? Она сама уменьшает есх и "лупит" — отсюда быстродействие. И где гарантия, что новых процессорах нет команд типа movsq и выше??

EW> Спасибо.

EW> EW


Не за что
вот тестовый примерчик — можно собрать и прогнать, а можно порассуждать логически:
void * p1 = malloc(4194304);
void * p2 = malloc(4194304);
// #1
unsigned long *l1 = (unsigned long *)p1[0], *l2 = (unsigned long *)p2[0];
for (int i = 0; i < 1048576; i++)
{
  *l2 =  *l1;
  l2++; l1++;
}

// #2
double *d1 = (double*)p1[0], *d2 = (double)p2[0];
for (i = 0; i < 524288; i++)
{
  *d2 = *d1;
  d1++; d2++;
}

ну, как вы думаете, что будет быстрее?
#1 — 4 байта за один шаг
#2 — 8 байт за один шаг...

Vadim VuDZ
Re: Копирование памяти
От: VuDZ Россия  
Дата: 24.12.01 17:37
Оценка:
Здравствуйте VuDZ, Вы писали:

Спасибо всем отвечавшим
особенно Alex Fedotov'у
если кого заинтерисовало то. о чём тут вели разговор — тут лежит библиотека с некоторыми стандартнми функциями, реализованные через MMX...
о реализованных функциях написано тут... Все пожелания на счёт добавления чего-ньть мыльте мне
Re: Копирование памяти
От: Речка Россия  
Дата: 04.01.02 14:06
Оценка:
Здравствуйте VuDZ и Alex Fedotov.
Очень интересно почитать вашу переписку не являясь знатоком asm'a.

А ведь наверное первое, что делает ваша программа,
использующая asm — это определение типа процессора?
С этого и все начинающие программеры на asm'e начинают.

Подскажите пожалуйста, как точно узнать тип процессора программно?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.