2011

О векторных расширениях gcc/clang (2)

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

К сожалению, предложенное там решение (офигенно быстрое!) считает неправильно, но направление движение указано верно и мы приходим к такому варианту:

  1. транспонируем матрицу, на которую умножаем, дополним нулями правую колонку, чтобы вышло 4x4
  2. Каждое из (четырех) входных значений - размножим на вектор.
  3. Нужный нам результат - это SIMD-сумма SIMD-произведений вышеупомянутых векторов на строки вышеупомянутой транспонированной матрицы.
Короче, проще кодом:

Одной строкой: mov[a|nt]ps

Не могу молчать:

Есть такие вот "ассемблерные" макросы:

  • _mm_store_ps(указатель, XMM-регистр) - писать в память.
  • _mm_stream_ps(те же параметры) - писать в память мимо кэша.

Для первого из них генерируется инструкция movaps (Intel C++ в некоторых условиях генерирует movntps, чему я удивлялся всегда). Для второго - всегда movntps ("писать мимо кэша"). В теории, при обработке больших потоков данных вторая быстрее.

Я неопытный сварщик SSE-ассемблером занялся не так давно, на рабочей станции был уже Core I7 и на Core2 я свои изделия (которые пока для internal use) - не запускал почти. А тут - запустил. Удивился тормозам. Помикробенчмаркал. На коротком цикле, вроде поминаемых тут скалярных умножений, получил разницу в четыре раза.

В том смысле, что movntps - в 4 раза медленнее. 40 мегапикселей вместо 160. Устойчиво, от компилятора не зависит, и на gcc так и на Clang.

В-принципе, от mm_stream выигрыша большого на i7 не было. Похоже, лучше про нее вообще забыть.

О компиляторах и процессорах: AVX

Армянское радио Нас спрашивают:

Как измениться производительность intrinsic варианта на Core-i7, если поменять
_mm_dp_ps на _mm256_dp_ps
_mm_blend_ps на _mm_blend256_ps

То-есть насколько вырастить производительность если мы совсем на AVX переедем и будет обрабатывать по 8 float за проход? А то слухи разные ходят... от 0% до 200% роста.

Отвечаем:

О компиляторах и процессорах

В комментариях к моему посту о целых числах и плавающей точке мне посоветовали обратить внимание на векторные типы данных и сравнить их по производительности со скалярными типами (ну и ручным SSE-ассемблером тоже).

Я обратил и сравнил, но в процессе получилась масса побочных результатов (разные архитектуры, разные компиляторы), которые жалко выкинуть, а хочется опубликовать.

На смерть бумажной прессы

Звонок:

Издательский дом Коммерсант, льготная подписка, до 15 ноября (sic!)

Отвечаю:

Мне очень жаль, но я что-то бросил вас читать, только копится непрочитанная бумага, больше не буду подписываться....

Поговорили еще минутку и попрощались....

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

О legacy и форматах данных

Есть устоявшееся мнение, дескать обработка (изображений) в целых (16-битных) числах - это быстро, а делать то же самое в плавучке - медленно.

А я вот тут читаю ассемблер, порожденный компилятором из C-шного кода обработки изображения. Много думаю.

Имею сказать, что распространенный в настоящее время формат "16-битное целое на компонент" - это максимально неудачный способ с точки зрения эффективности обработки:

  • от малейшего чиха переполняется или превращается в тыкву обнуляется, нужно постоянно следить за диапазоном;
  • векторные (SSE,AVX) операции с этим типом - очень ограничены, да и не векторные - тоже.
В результате, сплошные мучения компилятору, а результат - медленно работает. Скажем, вот такой вот код (из dcraw), который делает преобразование по матричному профилю для 3-4 входных каналов (цветов) и трех выходных:
out[0] = out[1] = out[2] = 0;
for (сc=0;сc<colors;сc++) {
  out[0] += out_cam[0][сc] * img[сc];
  out[1] += out_cam[1][сc] * img[сc];
  out[2] += out_cam[2][сc] * img[сc];
}
for(cс=0;сc<3;сc++) img[сc] = CLIP((int) out[cс]);
img[] - unsigned short, out[] - int, out_cam[] - float.

Смотрю на код, который порождает интеловский компилятор. Ну код, да. (три-)четыре load по 2 байта (количество loads зависит от colors /количества цветов/), дальше "вручную" выписанный dot product (нормальный, насколько это возможно), ну и обрезание до диапазона 0-65к и store.

Скорость работы этого кода - 100 мегапикселей в секунду на Sandy Bridge 4.5Ghz (в один поток, понятно что параллелится это на ура). Как-то не очень....

Да, считаем в мегапикселях т.к. в unsigned short у нас 8 байт на (4-компонентный) пиксель, а для float/int - 16 байт.

И еще о Linux (holy war!)

Что-то давно я Linux не троллил!

Когда мы делали Спамтест (который стал затем Kaspersky Antispam), чудовищным гемороем было изготовление дистрибутивов для Linux:

  • продукт поставлялся в бинарниках;
  • продукт интегрировался в MTA (штук 5 разных, от Sendmail до Qmail) путем патченья их конфигов;
  • Ну и хотелось поддержать, по возможности, все, что хоть как-то двигается.
С третьей задачей мы не справились, ибо количество способов, которыми расположены стартап-скрипты, конфиги и т.п. - поражало воображение. Имелся список поддерживаемых дистрибутивов (на которых сами тестировали, слава VMWare), а если у вас не оно - ну сами ставьте, документация есть.

Вчера я убедился, что за прошедшие 7-8 лет ситуация не изменилась. Ну вот драйвера для видеокарт ATI (которые бинарные). Казалось бы, ну kernel module, модуль в X11, библиотеки. Ну значит определи архитектуру, версию kernel (если драйвера разные), версию X11 - и ставься себе.

Однако хрен.

Linux A

Вы не поверите, но тулза про которую я спрашивал в прошлом посте называется GNOME System Monitor и делает ровно что надо: и цифрами и графиком и чаще раза в секунду и вообще. В GNOME-based системах должна быть просто с раздачи. Разные CPU можно красить в разные цвета, я просто поленился это делать для 24-х, да и цветов всяко не хватит.

Ей бы еще окошко с interrupt rate и вообще была бы лафа.

P.S. Подозревавшуюся проблему с affinity отловил, она там есть.

Linux Q

Граждане Линуксоводы!

Имею вопрос: есть ли к mpstat какой-нибудь GUI, ну вроде как в винде Performance Monitor? Мне надо смотреть load по ядрам, а то есть подозрение, что программа (чужая) в смысле affinity маленько безобразничает.

Дополнительное пожелание: уметь самплить чаще чем раз в секунду, 100-200 ms были бы в самый раз.

Про mpstat -P ALL я в курсе, только вот ядер - 24 штуки и никакая разумная история, ну хоть за 10 секунд, на экране никак не помещается.

I like to move it, move it

Мониторю всяческие новости про OpenCL, CUDA и прочие GPGPU и в последние дни просто засыпан новостью про то, что GEGL is getting GPU-based image rendering and processing.

Довеском к этой новости идет ссылка на OpenCL on GEGL: Results up to now, где сравнивается реализация brightness-contrast фильтра на CPU и на GPU (и не каком-то, а Tesla 2050 ценой 2 килобакса) и получается для 1-мегапиксельного изображения:

  • 526 msec на CPU
  • 483 msec на GPU
Просто гигантский выигрыш, почти 10% !!! Притом, как я понял (может быть и неправильно), во время исполнения на GPU не посчитана пересылка "обратно", с карты в RAM.

При этом, заметим, на CPU оно работает на одном ядре (хоть и на SSE2), а значит на 4 ядрах оно банально будет быстрее разика в три.

Причина тривиальна и прямо в том блог-посте описана, весь пар ушел в свисток, а все время исполнения - на пересылку данных. Собственно исполнение обработки на GPU занимает около 1/10 всего времени.

При этом, само время исполнения - чудовищно. Полсекунды на 1 мегапиксель, даже если пиксели 16-байтные (4 float) - это 32 мегабайта в секунду. Э.... По PCIe обычно ходит 4-5 гигабайт/сек..... Проблема, судя по всему, кроется в тайлововой организации картинки в GEGL, тайл при этом мелкий (128x64) и даже на CPU обрабатывать их эффективно не получается, что уж говорить про GPU, где под каждый тайл аллоцируется текстура.

На эту тему имею рассказать следующую историю:

Pages