Быстрое копирование видео памяти

Dmitry Serkin (Intel) (24 пост(а)) 07.05.2009 14:25

Тема оптимизации копирования памяти сильно изучена. Тут уже многое сделано, и улучшить ситуацию вряд ли представляется возможным. Стандартная функция memcpy при соответствующих настройках компилятора демонстрирует отличную производительность, не говоря уже о функциях копирования из специализированной библиотеки Intel(R) IPP.

Особый интерес вызывает тот случай, когда копирование происходит не между различными регионами системной памяти, а между локальной памятью дискретной графической карты и системной памятью. Ситуация меняется кардинально, и такое копирование, как правило, превращается в узкое место. Достучаться до «далекой» памяти видеокарты очень накладно.

Наша команда Media Core занимается разработкой и оптимизацией кодеков. Енкодеры и декодеры используют все преимущества железа, в частности, многие тяжелые вычисления исполняются на графических чипах через те или иные доступные интерфейсы. Процесс транскодинга, декодирующий данные на GPU и енкодирующий частично или полностью на CPU, требует колоссального количества копирования данных из видео памяти в системную. Простой синтетический тест показал, что 900 кратное копирование кадра (30 секунд видео) в разрешении HD (1920x1080) из видео памяти в системную на машине с процессором Nehalem занимает 12 секунд против 0.5 секунды при копировании такого же количества из системной памяти в системную. Заметим, что для копирования использовалась оптимизированная функция из библиотеки IPP. Что уж тут говорить про memcpy. 24х кратное падение производительности! Неприемлемый результат.

Так в чем же дело? И в чем причины такого замедления?

Дискретные и интегрированные адаптеры мапируются на виртуальные адреса доступные процессору в качестве памяти USWC (Uncacheable Speculative Write Combining). Память, как видно из название, некэшируемая. Почему некэшируемая? Потребителями USWC памяти являются, как правило, input-output устройства, в том числе видео адаптер. Так вот, у видео памяти страницы уж точно должны быть некэшируемые, иначе бы изображение обновлялось не в момент записи, а спустя некоторое время необходимое процессору на стратегию кэширования.

Очевидно, что работать с такой памятью в обычном режиме недопустимо. Автоматически распознавать к какому типу памяти мы обращаемся, тоже представляется мало возможным. Существует возможность достучаться к атрибутам страницы с помощью MTRRS (Memory Type Range Registers). Но не все процессоры поддерживают MTR регистры и не уверен, что это возможно на уровне приложения (скорее на уровне драйверов). Таким образом, оптимизация функции копирования памяти возможна лишь в виде добавления специальных флагов указывающих какую из веток оптимизации нужно исполнить. Однако, остается открытым вопрос самой оптимизации.

Очередной релиз Intel(R) Streaming SIMD Extensions 4 (Intel(R) SSE 4) расширился специальной потоковой инструкцией movntdqa, которая обеспечивает увеличение скорости чтения USWC памяти в 7,5 раз.

Использование команды чрезвычайно просто, но не лишено некоторых хитростей. Рассмотрим типичный пример:

movntdqa xmm0, oword ptr [src + 00h]
movntdqa xmm1, oword ptr [src + 10h]
movntdqa xmm2, oword ptr [src + 20h]
movntdqa xmm3, oword ptr [src + 30h]

add src, 40h ; advance source pointer

movdqa oword ptr [dst + 00h], xmm0
movdqa oword ptr [dst + 10h], xmm1
movdqa oword ptr [dst + 20h], xmm2
movdqa oword ptr [dst + 30h], xmm3

add dst, 40h ; advance destination pointer

Перед вами одна итерация чтения из USWC памяти в системную память. 64 байта считываются
потоковыми инструкциями из USWC памяти и записываются стандартным movdqa в системную память. Мешать инструкции чтения и записи крайне не рекомендуется, так как это приведет к потери производительности.

Сразу оговоримся, что априори считается, что вся память выровнена по длине кэш-линии (64 байта).

Давайте рассмотрим подробнее, что происходит на самом делe.

movntdqa – так называемая, инструкция потокового чтения мимо кэша. Таким образом, USWC данные не отображаются на кэш и попадают сразу в буфер записи. Далее системная память копируется в кэш, летит в доступный буфер записи и происходит копирование между буферами. Последние процессоры Intel имеют 4 буфера записи на ядро. Буфера записи – это тема для отдельной статьи. Сейчас же ограничимся этими знаниями.

Несмотря на то, что данные пересылаются пакетами по 64 байта, на постоянные переключения состояний шины памяти затрачивается огромное количество тактов. Нужно придумать схему, которая позволит минимизировать затраты на переинициализацию шины памяти.

Такая схема существует. Ключевой момент – это использование промежуточного 4 кБ кэшируемого буфера (размер подобран опытным путем). На первой стадии буфер полностью заполняется USWC данными, как в примере выше. На второй, происходит копирование из буфера мимо кэша в системную память. Из буфера читаем стандартной movdqa, в память читаем мимо кэша (movntdq), так как данные уже лежат в буферах записи.

movdqa xmm0, oword ptr [buf + 00h]
movdqa xmm1, oword ptr [buf + 10h]
movdqa xmm2, oword ptr [buf + 20h]
movdqa xmm3, oword ptr [buf + 30h]

add buf, 40h ; advance source pointer

movntdq oword ptr [dst + 00h], xmm0
movntdq oword ptr [dst + 10h], xmm1
movntdq oword ptr [dst + 20h], xmm2
movntdq oword ptr [dst + 30h], xmm3

add dst, 40h ; advance buffer pointer

Небольшое видео для иллюстрации процесса:

Приведем результаты. Тестирование проводилось на двух платформах с различными графическими адаптерами. Сравнивались два режима:

1. Режим GFX - копирование из видео памяти в системную.

2. Режим SYS - копирование из системной в системную память. Тоже рассматривается, так как ожидается увеличение производительности за счет потоковых инструкций.

Как видно из графика, функция дает 6-кратный прирост производительности. SYS режим также показал небольшой прирост.

Дополнительный материал по теме

Категории: Графика, Разработка софта

Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.

Комментарии (48)

07.05.2009 05:51

Dmitry Oganezov (Intel)
Dmitry Oganezov (Intel)Всего баллов:
25,473
Community Manager
Дима, Welcome to ISN Blog!!! С почином!

И сразу ряд вопросов, только пожалуйста мне на пальцах - как для чайника: "Простой синтетический тест показал, что 900 кратное копирование кадра (30 секунд видео) в разрешении HD (1920x1080) из видео памяти в системную на машине с процессором Nehalem занимает 12 секунд"

Можно подробнее с этого места? На 30 секунд видео уходит почти 12 секунд на передачу данных? На i7? И сколько там данных? Неужели при транскодировании нужно кидать все кадры в полном объеме?

И еще одно мааалюсенькое замечание. Давайте не будем называть Nehalem "Nehalem-ом". Этот процессор называется Core i7 :)
07.05.2009 06:18

Dmitriy Vyukov
Dmitriy VyukovВсего баллов:
43,814
черный пояс
Про промежуточный буфер не до конца понятно.
Вначале 4К читаются из видео памяти в промежуточный буфер как:
movntdqa xmm0, oword ptr [src + 00h] ; тут NT считывание
...
movdqa oword ptr [buf + 00h], xmm0; а тут уже обычная запись

Потом 4K пишутся из промежуточного буфера в конечное назначение в основной памяти как:
movdqa xmm0, oword ptr [buf + 00h] ; тут обычное считывание
...
movntdqa oword ptr [dst + 00h], xmm0; а тут уже опять NT запись

Я правильно уловил идею?
07.05.2009 06:20

Dmitriy Vyukov
Dmitriy VyukovВсего баллов:
43,814
черный пояс
А можно ещё поподробнее про "переключения состояний шины памяти". Они как-то связаны с тем, что работаем с устройством? Или же они будут и при работе с основной памятью, если будут чередоваться запросы на чтение и запись?
07.05.2009 06:20

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Да. Именно 12 секунд. И дело тут в природе некэшируемой памяти. Ну всего во время теста как ты понимаешь перегоняется 900x1920x1080 байт. Синтетический тест не подразумевает под собой процесса декодирования, поэтому видео память инициализирована "мусором". Для реального декодинга уже тоже есть очень хорошие результаты.

Да, железо нуждается в полной информации (хидера, битстреам ... ). Если фрейм progressive, то он декодируется за один вызов, если филд, то за два вызова (для каждого филда отдельно).
07.05.2009 06:33

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Дмитрий, совершенно верно. Только в конце movntdq. Видео отлично иллюстрирует этот процесс :)

Буфера записи и шина памяти заслуживают отдельных статей. Думаю напишу еще. Вкратце, будут проблемы и с обычной памятью. Постоянная переинициализация новыми адресами (поленился нарисовать график ступенчатый функции с малыми шагами) требует достаточно большого количества тактов. Гораздо эффективнее, когда происходит несколько транзакций последовательных адресов (шаг функции увеличивается).
07.05.2009 06:41

Dmitry Oganezov (Intel)
Dmitry Oganezov (Intel)Всего баллов:
25,473
Community Manager
Так, во-первых нас тут трое Дмитриев. Давайте, что ли, хеш-фунцию по фамилиям включим для ясности :)

Во-вторых, я так и не понял природы "раскодирования" на GPU+CPU: по наивности всегда полагал, что обмен происходит далеко не полным кадрами, а их фрагментами. Теми кусочками, которые поменялись. Так что просьба: написать отдельный "вводный" пост (нулевой эпизод), поясняющий терминологию, суть процесса, и роль GPU. А потом вернемся к видеопамяти ;)
07.05.2009 06:48

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Как скажешь :) Планирую большую серию статей про motion estimation (алгоритм оценки движения) на GPU Nvidia, Ati и Intel. Ну и про DXVA2 тоже могу написать, что знаю. :) Так что пожелай мне удачи :)
07.05.2009 06:52

Dmitry Oganezov (Intel)
Dmitry Oganezov (Intel)Всего баллов:
25,473
Community Manager
Удачи! Да пребудет с тобой Сила! :)
07.05.2009 09:50

ilnarb
ilnarbВсего баллов:
5,647
коричневый пояс
Опуская приложение в виде кодирования, можно придумать другую задачу, которая общается с GPU.
Благо использование GPU в качестве рабочей лошадки сейчас растет. И некоторые алгоритмы требуют частого обмена данных м/у GPU и CPU.

А можно ли получить оценки прироста скорости для карт Nvidia? меня CUDA больше привлекает))
Я правильно понял что SSE4 он с Core i7?

Еще один праздный вопрос, но уже не вам, быстро ли Nvidia в CUDA включит ваш код?))
07.05.2009 10:32

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Сложный вопрос. Думаю, что они уже давно включили поддержку потоковых инструкций на уровне драйверов. Потом я даже где то читал длинный тред о том, что быстрее DMA (Direct Memory Access) или movntdqa. Насколько помню консенсус не был достигнут.
07.05.2009 13:56

Dmitriy Vyukov
Dmitriy VyukovВсего баллов:
43,814
черный пояс
Re: Видео отлично иллюстрирует этот процесс :)
Слона-то я и не заметил... :)

Re: Буфера записи и шина памяти заслуживают отдельных статей.
Про буфера записи я примерно представляю, поэтому я голосую за шину памяти.
08.05.2009 16:15

Dmitriy Vyukov
Dmitriy VyukovВсего баллов:
43,814
черный пояс
Re: Про буфера записи я примерно представляю, поэтому я голосую за шину памяти.

Посмотрев-таки видео, я понял, что не представляю, что такое буфера записи. Я думал, что речь идёт о write-combining буферах. Но насколько я знал, они никоим образом не используются при загрузке данных. В видео же через них идёт загружаемые в процессор данные...
А как буфера записи называются на английском?
09.05.2009 16:34

Maxym Dmytrychenko (Intel)
Maxym Dmytrychenko (Intel)Всего баллов:
2,105
коричневый пояс
понять где какой тип кеширования памяти просто:
- в Linux - cat /proc/mtrr
и дальше стоит почитать например тут http://www.filewatcher.com/p/kernel-source-2.2.10_2.2.10.ori.....r.txt.html
- Windows - например SiSoftware Sandra

меняются MTRR через MSR, Model-Specific Registers – модельно-специфические регистры вот здесь и надо доступ к Kernel Mode для приложение, тоже самое для PAT - Page Attribute Table
10.05.2009 04:29

Dmitriy Vyukov
Dmitriy VyukovВсего баллов:
43,814
черный пояс
В Windows можно установить тип кэширования памяти и атрибуты страниц с помощью VirtualAlloc() из режима пользователя. Функция принимает много всяких интересных флажков как PAGE_READWRITE, PAGE_READONLY, PAGE_WRITECOPY, PAGE_GUARD, PAGE_NOCACHE, PAGE_WRITECOMBINE.
12.05.2009 01:37

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Все вышеперечисленное, конечно, верно. Но, как мне кажется, вне концепции библиотеки IPP. Я не являюсь разработчиком библиотеки, поэтому могу только догадываться. Гораздо проще и прозрачнее добавить несколько флагов к функции, которые будут определять ее поведение (стриминг или нет). Что вообщем то и было сделано. И самые новые версии библиотеки будут иметь специальную функцию, использующую все преимущества стриминга.

Наиболее легким путем мне казалось использование MTRR, однако MSR регистры доступны только на 0 кольце.
23.07.2009 01:38


Flame
А можно поподробнее о USWC? И как с этим работает процессор в обычных операциях чтения/записи? И как быть если нету SSE 4?
26.07.2009 11:27


Flame
C USWC вроде разобрался, это некэшируемая память с пакетной записью. Если правильно понял, то чтение тоже некешируемо, а вот это не всегда хорошо и часто является тормозом. Дело даже скорее не в кэшировании, а в префетче, а именно в его отсутствии. Но тогда не понятно, каким образом SSE4 инструкции могут ускорить процесс. Хотя по описанию - "movntdqa – так называемая, инструкция потокового чтения мимо кэша", смахивает на чтение мимо кэша с префетчем. Вообще странно, почему эти инструкции не появились раньше, ведь наверно эта проблема появилась не вчера. И остаётся вопрос, что же всё таки делать если SSE4 недоступно? Остаётся смириться с этим прискорбным фактом?
05.08.2009 11:05


Flame
Все ушли на войну? По-моему эта тема сферческий конь в вакууме. Есть ли тут смысл для прикладного программирования? Или это ценно только для драйверописателей? Неужели блокировка офлайновой поверхности(DirectX) возвращает USWC память?
18.09.2009 09:12

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Ого :) Я то думал мне оповещения обо всех комментариях приходят. В любом случае, лучше поздно, чем никогда.

Есть ли тут смысл для прикладного программирования?

Определенно есть, когда вы лочите поверхность и получаете указатель на видео память. И эти данные нужно скопировать во внутренние системные буфера. Написанная вами функция копирования памяти с использованием вышеозначенных инструкций позволит ускорить данный процесс в несколько раз в сравнении с стандартными библиотечными функциями копирования. Причем, чем больше кусок памяти вы хотите скопировать, тем больший прирост ускорения.

И остаётся вопрос, что же всё таки делать если SSE4 недоступно?

Сложно сказать. Возможно стоит попробовать использовать DMA контроллер, если есть такая возможность.

Характерно, что я в своей практике встречал разное поведение, в смысле кешируемости видео памяти. Один интегрированный чип показывал одинаковые результаты, как для SSE4 функции, так и для стандартных подходов к копированию. Все зависит от железа.
12.11.2009 01:45

Timur Balbot
Timur Balbot
Дмитрий,

Вот вы пишите, что можно ускорить процесс копирования памяти в несколько раз в сравнении со стандартными библиотечными фунцкиями копирования.
К сожелению, мои тесты показывают совсем другие результаты. Я использую видеодекодер c поддержкой DXVA 2.0. Мне нужно забирать декодированную картинку NV12 из видео памяти для дальнейшего траскодирования. Для этого я вызываю lockrect для поверхности, копирую данные в системную память, вызываю unlockrect для поверхности. Замеры производительности показали, что на постоянные вызовы lockrect/unlockrect для поверхности тратиться большее время, чем на копирование из видеопамяти. В результате, нет никакой разницы между использованием обыченой ф-ции memcpy, ф-ции копирования с использованием movntdqa или movdqa.
Можете ли вы прокомментировать эти результаты?

12.11.2009 01:53

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
А какое железо используется (видеоадаптер, процессор)? LockRect очень специфичная функция и от драйвера к драйверу работает по разному. Сама по себе она представляет собой некоторый объект синхронизации, а значит в зависимости от реализации драйвера может занимать разное время на исполнение.

Приведенные результаты валидны только для последних чипсетов Intel.
12.11.2009 02:02

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Пардон, хотел написать для последних процессоров Intel с поддержкой SSE4.
Если вы считаете, что hot spot в функции LockRect, то это проблема реализации драйвера.
26.11.2009 01:22

Timur Balbot
Timur Balbot
Processor: Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz (4 CPUs), ~2.8GHz, Codename: Yorkfield
Card name: NVIDIA GeForce 8600 GT (G84, Rev A2), 256 MB GDDR3
Chipset: codename Eaglelake, получен по программе Intel Software Development Platform
ID2E00 Rev 2.0. Судя по http://en.wikipedia.org/wiki/Intel_GMA похоже что это Q43 (судя по Device ID).
Driver version: 9.0.0.1006 (Intel)

Ожидал, что в почту упадет ответ. Хорошо, что заглянул сюда.
26.11.2009 21:26

Timur Balbot
Timur Balbot
Собственно, задача у меня следующая.
У нас есть видеодекодер с поддержкой DXVA 2.0. Тестовая программа кормит декодер элементарным видео потоком из файла порциями данных. После того как картинка декодированна, я делаю примерно следующие операции:

hr = pIDirect3DSurface9->LockRect(&d3d_locked_rect, NULL, D3DLOCK_READONLY);//Лочим поверхность для чтения
if (hr == S_OK) {
memcpy_movntdqa_sse4(src, d3d_locked_rect.pBits, d3d_locked_rect.Pitch, height);// Копируем данные
pIDirect3DSurface9->UnlockRect();//Анлочим поверхность
}
convert_nv12_to_yv12(dst, src, width, height)//Конвертируем кадр из NV12 в YV12

Больше я пока ничего не собираюсь делать с декодированной картинкой. Я просто замеряю скорость декодирования в fps. Закомментируя в коде разные строки, я получаю следующие результаты:

LockRect+movntdqa+UnlockRect+conversion = 166,49 fps
LockRect+movntdqa+UnlockRect = 177,03 fps
LockRect = 184,79 fps
Decode only = 280,08 fps

Получается, что:
LockRect+UnlockRect съедает 34,02 %
Copying data by using movntdqa съедает 2,77 %
Color conversion from NV12 to YV12 съедает 5,07 %

Вывод:
Вызовы LockRect+UnlockRect отнимают в 12 раз больше в сравнии с копированием декодированной картинки из поверхности во временный буфер в системной памяти.
Также я не заметил никакой разницы между memcpy, movdqa, movntdqa.
Похоже на то, что мы копируем как будто не из USWC памяти, а из системной. Однако, GetDesc() для поверхности возвращает тип памяти D3DPOOL_DEFAULT, что из DirectX SDK Documentation означает:
"This is usually video memory, including both local video memory and AGP memory."

Полученные мной результаты, к сожалению, никак не соотносятся с описанными выше.
27.11.2009 03:46

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
В данном случае bottleneck я вижу вовсе не в копировании, а в получении выходных декодированных поверхностей. Так как декодирование происходит на адаптере стороннего производителя, то я не могу дать никаких официальных объяснений, все зависит от драйвера. Что касается приведенных в моей записи замеров, то это цифры простого синтетического теста, который производит запись и копирование видеопамяти. Процесс декодирование вполне может влиять на общую производительность копирования.

Ситуация, когда память воспринимается не как USWC, а как обычные системные страницы возможна. Я встречался с таким поведением, правда, в случае интегрированной графики.

Что касается ваших замеров, то валидны из них только два: LockRect+movntdqa+UnlockRect+conversion,
LockRect+movntdqa+UnlockRect. Еще можно третий добавить: LockRect+UnlockRect. Причина в том, что пока не вызывается функция LockRect, внутри которой происходит синхронизация, никто не гарантирует, что поверхность готова. То есть там может быть муср, либо частично декодированные данные.

Я предлагаю Вам посмотреть на продукты с открытым исходным кодом, которые поддерживают декодирование на Eaglelake (G45) и Ironlake чипсетах и оптимизировать свой детище под них.
02.05.2010 15:38

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
А можно ещё раз по-народному, за счёт чего копирование через буфер быстрее, скажем, вот такого?

movntdqa xmm0, oword ptr [src + 00h]
movntdqa xmm1, oword ptr [src + 10h]
movntdqa xmm2, oword ptr [src + 20h]
movntdqa xmm3, oword ptr [src + 30h]

add src, 40h

movntdq oword ptr [dst + 00h], xmm0
movntdq oword ptr [dst + 10h], xmm1
movntdq oword ptr [dst + 20h], xmm2
movntdq oword ptr [dst + 30h], xmm3

add dst, 40h


В чём тут колдунство?

03.05.2010 00:49

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Никакого волшебства. Просто уменьшаем давление на буфера и не загрязняем кэш.

В вашем случае происходит соперничество за буфера. В одном цикле вы читаете и пишете. В результате больший оверхеад, загрязнение на кэш.

Если же использовать промежуточный буфер, выделенный на L1, то в первой части цикла мы всегда пишем в него 4K, во второй читаем из него, и так как буфер опрашивается часто, то оверхеад на доступ к нему очень мал. К тому же при заполнении и выгрузке буфера используется потоковая инструкция только одного типа и соперничества не происходит.

Рассматривайте данных подход как best knowledge method.
03.05.2010 10:34

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Реализовал оба подхода. При подходе "читаем мимо кэша - сразу пишем мимо кэша" получил скорость немного повыше, чем с копированием через буфер... (копирование из системной в системную, проц - Q9400). В принципе, ещё до Вашей статьи методом тыка заметил, что некоторого прироста можно добиться, если писать в память из xmm "мимо кэша". При этом, танцы с бубном типа развёртывания циклов или "более кошерное", чем по 16 байт, выравнивание прироста в скорости не дают.

Кстати, при использовании movntdq, опять же надо быть осторожным! Скажем, на HP Compaq 6820s (проц - T5470), скорость копирования упала раз в 10! Ну, и ещё одно наблюдение... При копировании с movdqa из памяти в xmm-регистры и из них - в память, скорость абсолютно такая же, как в тупом сишном for(int i=0; i<N; i++)a[i]=b[i];

Вообще, если честно, то увеличение скорости копирования память-память в сравнении с IPP я лично (в силу повышенной скептичности :) ) отношу на то обстоятельство, что IPP-шной функции ещё нужно понять, что и адрес истоника, и адрес приёмника выровнены по 16 байт, и она имеет дело с наилучшим случаем.... Добавьте в свою функцию копирования проверку "на все случаи жизни" - и, возможно, преимущество тут же растает....

Немного оффтопика... Просто интересно, как волшебники из Intel считают медианный фильтр? Ну, хотя бы, с квадратной аппертуркой... Потому что алгоритмов всяких хороших дохрена, в том числе, и константные, но если не фильтровать окном, ну, там, 1000х1000, и изображение у нас не 16-цветное, скажем, толку от этих "хороших" алгоритмов - 0!

03.05.2010 10:52

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
А зачем не по назначению инструкции использовать? Я использую данный подход только для копирования USWC памяти и получаю 18 кратный прирост. Для случая системной памяти уже все заоптимизированно окончательно и давно.

В моем случае функции с буфером работаем немного быстрее. Сам тоже использую прямой метод из IPP, есть функция ippCopyManaged.

Согласен, что от железа многое зависит. Также наблюдал некоторые метаморфозы.

Я сам не разработчик IPP, но вашу скептичность полностью поддерживаю. :) Именно так там дело и обстоит, по моим ощущениям и опыту. С другой стороны, память надо выравнивать, если уж о производительности беспокоитесь! :)

Насчет медианного фильтра ничего сказать не могу :) Советую написать в IPP ветку на форуме. И вам там ответят.

Спасибо за интересные комментарии!
04.05.2010 00:35

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Кстати, grandvic, спасибо за наводку. Попробую в будущем реализовать этот фильтр, обработка сигналов очень интересная мне тема.
04.05.2010 05:38

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Не за что :) Вообще, обработка изображений и нечёткие системы - мой профиль :)

Кстати говоря, свёртка - тоже, на мой взгляд, нуждается в оптимизации... Тут даже математики человеческой нет! Все методы, которые я видел, уже, на мой взгляд, безнадёжно устарели... Основная мыль - "вот, у нас было на пиксел изображения 100 умножений и 99 сложений, мы предложили круто оптимальный алгоритм, позволяющий вычислять это всё за 4 умножения и 296 сложений" -- но насколько я для себя уяснил (эмпирическим путём :) ) - умножения и сложения на современных процессорах выполняются примерно за одинаковое время (кстати, прав ли я?)

Я, лично, реализовывал без всех этих математических штучек (потому как толку - ноль, и плюс ко всему - фактически хаотическая работа с памятью), при этом, пытался минимизировать количество записей типа регистр-память, память-регистр... Естественно, имеется в виду свёртка "в общем виде" - там, где фильтр сепарабельный, и, плюс ко всему, представляется рекуррентными соотношениями, т.е. существуют константные алгоритмы - понятно, что проблем особых нет...

Кстати, чисто теоретически, в сравнении с Си-шным кодом какое максимальное ускорение можно получить при использовании SSE-расширений? У меня лучше всего плучилось ускорить нечёткую морфологию (при удачных размерах маски - раза в 32)...
04.05.2010 05:47

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
И ещё немного оффтопика :-D

О "метаморфозах".... В глубоком детстве использовал встроенный ассемблер для оптимизации (Visual Studio 2000-какая-то). Осталась одна большая загадка. Значится так. Сравниваем два кода: чистый сишный и с использованием встроенного ассемблера. Компилирую в Release - ассемблерный немного быстрее... Компилирую в дебаге - сишный код в дебаге работает медленнее, чем сишный в релизе -- ну, как говорится, и слава богу... НО! код с использованием встроенного ассемблера ............ в дебаге сработал БЫСТРЕЕ, чем тот же код в релизе! Пойти в церковь - поставить свечку, или это нормально? :-D
04.05.2010 05:53

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Сложение и вычитание хорошо конверизуемые операции и выполняются за 0.5-1 такт. Умножение за 3-5 тактов, деление в 3-5 раз больше. Это, если мы говорим о целочисленных; с плавающими все, очевидно, гораздо сложнее. Так что считайте сколько ребята тактов экономят на свертке :)

Теоретически сложно сказать. Понятно, что в разы. Да только кому теперь нужны такие данные, если самая наистандартнейшая функция memcpy развернется на тех же самых sse инструкциях microsoft компилятором. А интеловский вообще конфетку из нее сделает. :)

Интересная у вас работа! )
04.05.2010 06:02

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Это вполне запросто и нормально.

В дебаге выравниваются адреса и код. ALIGN 10h ;)
04.05.2010 10:12

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Ну, деление - да! Оно и в африке - деление... По поводу сложений умножений - по моим замерам, пакетные версии, фактически, одинаково быстро считаются (складывалисьумножались 2 больших массива). Ну, а по поводу "сколько выигрывают" - думаю, тут только проиграть можно... Ну, а даже теоретический выигрыш в пару тактов, имхо, полная фикция в сравнении со скоростью чтения некэшированного куска памяти :-D


> Да только кому теперь нужны такие данные, если самая наистандартнейшая функция memcpy
> развернется на тех же самых sse инструкциях microsoft компилятором.

Отставить панику!! :-D Я эту "magic button" под названием "use simd instructions" или кака там оно называется -- даже не трогаю! Посмотрел пару раз ассемблерный код - ...... они SSE используют...... для конвертирования, скажем, int --> float 8-( ) И ВСЁ!!! Хотя в том коде, который смотрел, стримить и параллелить было можно и нужно, фактическс, всё! При чём, несмотря на то, что всё это дело крутилось в цикле --- упорно использовали не пакетные, а single-операции конверта!!! Так что на специфических задачах - в частности, на операциях цифровой обработки сигналов -- можно и нужно выжимать из железок всё!

Ну, а в целом - согласен... Добиться кода, который будет "рвать как тузик грелку" сишный код, скомпилированный нормальным компилятором - слабо реально. Более того, можно получить и более медленный код.

Кстати, о рыбках... Можете по-народному рассказать, что делает команда loop? И почему она такая тормозная? В частности, на процессорах Intel? (AMD отбросил в ОЧЕНЬ глубоком детстве)
04.05.2010 10:48

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
> Это вполне запросто и нормально.
> В дебаге выравниваются адреса и код. ALIGN 10h ;)

%) брррр! Ну, данные - и у меня были выровнены.. (детство было не совсем глубоким :-D ) А по поводу кода - ... неужто он не кэшируется???

Кстати говоря, если не сложно... Не могли бы Вы прояснить ситуацию с работой кэша? (ну, хотя бы, на уровне L1)? А то полез в инет -- и давай искать! А фразы типа: "если загружаемый участок памяти ещё не в кэше -- процессор помещает его в одну из линеек" -- интригуют!!! Ну, скажем, потоком читам память в Вашем алгоритме.... И чем пару десятков тактов назад загруженный участок - не "одна из линеек"? Не, ну, следуя здравому смыслу и тезису о высоком уровне компетентности инженеров из Intel понимаешь, что это будет не она, но, всё же, хотелось бы уточнить :-)

Просто после данной статьи начинают идти интересные мысли - в частности, можно те же "нехорошие" операци ЦОС переписать, используя L1, как ту же shared память на GPU.... Блин! И снова блочность!..... Нахожу много общего между nVidia GPU и CPU от Intel :-D В плане математики, методы чисто на уровне интуиции подозреваю, что методы будут одни и те же.... Ну..... это как машина Тьюринга и машина Чёрча.... Как Маркс и Энгельс :-D

А, вообще, если есть желание скооперироваться -- по тому же медианному фильтру -- я очень даже "за!" :)
04.05.2010 13:35

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Выравнивание кода достаточно важная вещь. И связано это, прежде всего, с особенностью декодирования инструкций процессором. Инструкции считываются и декодируются по 16 байт. Соответственно, если цикл, содержащий много инструкций не выравнен на 16, то образуется ощутимый оверхед лишнего чтения и декодирования.

loop - это псевдоним для двух последовательных операций:
sub ecx 1
jnz CYCLE_LABEL

Уменьшают количество данных для декодирования. Почему медленная? Потому, что тяжелая наследственность и не вообще ее использовать не рекомендуется. Вот и замедлили :) Шучу. Надо бы по мануалам порыскать. Не используйте ее.

Кто-бы мне прояснил как кэш работает :) На пальцах могу, да только вы моих пальцев не видите :) Я подумаю над вашим предложением.

Медийный фильтр пока подождет. Сейчас я пытаюсь найти время для Seam Carving алгоритма. Пишу его в реальном времени попутно оформляя блоги на эту тему. Вот первый пост: http://software.intel.com/ru-ru/blogs/2010/04/28/seam-carving-intro/

Присоединяйтесь! ) Я уверен, что у вас есть целый лист алгоритмов, что так меня интересуют в этой первой заметке о Seam Carving. Буду рад Вашим комментариям.
04.05.2010 19:33

ksili
ksiliВсего баллов:
7,570
коричневый пояс
> Кстати говоря, свёртка - тоже, на мой взгляд, нуждается в оптимизации...
> Я, лично, реализовывал без всех этих математических штучек (потому как толку - ноль, и плюс ко всему - фактически хаотическая работа с памятью), при этом, пытался минимизировать количество записей типа регистр-память, память-регистр... Естественно, имеется в виду свёртка "в общем виде" - там, где фильтр сепарабельный, и, плюс ко всему, представляется рекуррентными соотношениями

Почему работа с памятью хаотическая? Память фильтра, которая используется постоянно, находится в одном месте. Сворачиваемые последовательности читаются из памяти последовательно, при этом элементы необходимые на каждом этапе свёртки почти читались на предыдущем этапе, а значит скорее всего, при правильной организации алгоритма, будут уже в кэше.
Из-за того, что формула рекуррентная, при помощи SSE наверно получится либо распараллелить только каждый отдельный этап свёртки (если длина меньшей последовательности кратна кол-ву элементов, влазящих в регистр SSE, то супер), либо реализовать одновременно несколько сверток.

Всё, что я написал выше, чисто теоретические рассуждения :-))
04.05.2010 19:34

ksili
ksiliВсего баллов:
7,570
коричневый пояс
исправление:

почти читались --> почти ВСЕ читались
05.05.2010 00:30

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
Ksili,
В теории мы все сильны. Я вообще обожаю разговаривать о том, в чём ничего не понимаю :)

Grandvic,
Кстати, о GPU. Я как то решал одну задачу аж на 3 разных gpu. Блочность - краеугольный камень программирования на gpu. Вот тут можно кое-что прочесть http://software.intel.com/ru-ru/blogs/2009/06/10/gpu-cuda-st..... -larrabee/
05.05.2010 02:48

ksili
ksiliВсего баллов:
7,570
коричневый пояс
> В теории мы все сильны. Я вообще обожаю разговаривать о том, в чём ничего не понимаю :)

Значит давайте поговорим о свёртке ;) не понимаю, где там "хаотическая работа с памятью". Расскажите мне, если вы понимаете.
05.05.2010 05:38

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Да, бога ради! Циклическая 5-точечная свёртка за 10 умножений и 31 сложение.... Сворачиваем d и g: s = d(*)g.

D0 = d0-d4
D1 = d1-d4
D2 = D0+D1
D3 = d2-d4
D4 = d3-d4
D5 = D3+D4
D6 = D0-D1
D7 = D1-D4
D8 = D2-D5
D9 = d0+d1+d2+d3+d4+d5

Вектор G - посчитан априорно на основании вектора g. Далее S = G*D (покомпонентное перемножение).

Подготовка к вычислению ответа:

T0 = S0+S2
T1 = S1+S2
T2 = S3+S5
T3 = S4+S5
T4 = S6+S8
T5 = S7+S8

s0 = T0-T4+S9
s1 = -T0-T1-T2-T3+S9
s2 = T3+T5+S9
s3 = T2+T4+S9
s4 = T1-T5+S9

Хаотическая - не в том смысле, что читаем постоянно из разных углов изображения, а в том смысле, что пакетные операции особо-то не применишь....
05.05.2010 06:13

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
По поводу сепарабельных и представимых в рекуррентной форме - абсолютно точно! Сразу считать несколько свёрток. При чём, как при горизонтальных, так и при вертикальных "пробегах"...

Кстати, интересный вопрос... Что страшного в команде movdqu ? Ну, имеется в виду, что делать целесообразнее?

2 х movdqa, и скомбинировать - скажем, при помощи palignr, или просто честно вызвать movdqu, которая, насколько я понимаю, делает то же самое? (это на случай, если размер маски не кратен вместимости регистров xmm :) )
05.05.2010 06:17

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
> loop - это псевдоним для двух последовательных операций:
> sub ecx 1
> jnz CYCLE_LABEL

Угу... Когда-то давно, когда ещё компьютеры были большими :-D писали:

dec cx
jnz near CYCLE_LABEL

аналогичиный вопрос, зачем нужны инструкции dec и inc , если всё равно пишут sub ..., 1 --- add ..., ?

05.05.2010 06:25

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
> Сейчас я пытаюсь найти время для Seam Carving алгоритма

Смотрел. Идея понравилась :) Но всё жутко субъективно... В абсолютно автоматическом режиме - пожалуй, вряд ли получится реализовать.... Так что скорее всего, прийдётся очень жёстко и настойчиво взаимодействовать с пользователем, или как его ещё называют "лицом, принимающим решение" :)
05.05.2010 08:00

Dmitry Serkin (Intel)
Dmitry Serkin (Intel)Всего баллов:
3,945
коричневый пояс
movdqu некоторый алиас, при декодировании распадается на микрооперации чтения пересекающихся 16 байтных кусков, конкатенацию и загрузку в результирующий регистр. Ресурсоемкая операция. Поэтому лучше делать так как вы написали.

dec/inc - бремя наследственности опять же. Декодируется процессором быстрее, чем add/sub, так как занимают меньше памяти, но выставляют не все флаги. С точки зрения быстродействия конвейера и чтобы процессор не откатывал историю, лучше использовать add/sub. Никаких dec/inc.

С seam carving есть над чем подумать :) Только давайте будем обсуждать это в нужной записи, а то тут прям каша уже образуется )
09.05.2010 17:36

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Ещё один чайниковский вопрос... Как быстрее всего разделить много целых чисел на целую контанту и привести к байту? (в общем случае, делитель не является степенью двойки)

Короче говоря, нужно какое-нить пкетное целое деление. Гарантируется, что результат деления "влазит" в диапазон от 0 до 255...

Пока ничего умнее не придумал, чем пакетно сконвертировать во float, пакетно умножить на заранее заготовленную константу, равную 1<то, на что делим>, и пакетно "домой" в int...

Маразм, конечно... Но в системе команд пакетного целого деления я не нашёл :(
Интересно, это лыжи не едут, или чего-то тут не то?
10.05.2010 00:36

grandvic
grandvicВсего баллов:
105
Зарегистрированный пользователь
Решено! :) Целое PMULLD умножение на 10000H / <делитель> + сдвиг влево на 16 бит

Обратная ссылка (2)


Оставить комментарий  

Для получения технической помощи посетите сайт службы поддержки.
Имя (обязательно)*

Электронная почта (обязательно; не будет отображено на этой странице)*

Ваш URL-адрес (необязательно)


Комментарий*