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, где под каждый тайл аллоцируется текстура.

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

Волею судеб, развлкался с Linpack Benchmark на видеокарте ATI/AMD. Естественно, в первую очередь с HPL-GPU, о чем напишу как-нибудь в другой раз. Но до кучи попробовал и другие всякие варианты.

С CPU все просто. Теория обещает нам 75% от производительности DGEMM (в идеальном случае). Теоретическая производительность у моего горшка - 48 гигафлопс (4 ядра * 4 инструкции/такт * 3 гигагерца). Берем GotoBLAS2, берем HPL-2.0 с нетлиба, компилируем, пускаем, видим 32-35 GFLOP/s в зависимости от размера задачи, все как и должно бы быть.

Дальше берем ACML-GPU, пускаем ейный time_dgemm и видим 220+ GFLOPS/s для матриц от 8k и 50GFLOP/s для матриц размером 1k. Это со всеми временами пересылок туда-сюда.

Радуемся, линкуем HPL с этой библиотекой и предвкушаем:

  • Или 75% от DGEMM т.е. в районе 160 GFLOP/s на Linpack
  • Или то, что мы собственно DGEMM за-оффлоадим на GPU и все упрется в "остальные 25%" т.е. получим примерно 4-кратное ускорение т.е. 140 GFLOP/s
Однако действительность оказывается хуже и максимум, которого удается достичь - 88 GFLOP/s. Несколько обидно для карты, которая по формальным спекам этих гигафлопсов имеет более 500.

Причин несколько:

  • Никто ничего не за-оффлоадил в том смысле, что пока GPU-шный DGEMM жует свои данные, все остальное - простаивает.
  • Для маленьких размеров матриц, похоже, используется CPU. Во всяком случае, так написано в анонсе ACML-GPU
  • Есть впечатление, что CPU-функции в ACML-GPU работают на одном ядре, а не на всех.

Реально же эффективная реализация (помянутый выше HPL-GPU) на том же самом железе втрое быстрее. В первую очередь, по той тривиальной причине, что все составляющие конструкции (CPU, GPU и DMA engine) работают одновременно, а не поочередно.

Comments

Совсем будет интересно, когда опенсорсный OpenCL допилят (который clover).
Его в рамках gsoc клепают, правда там пока только CPU имплементация (llvm/jit), а GPU обещают сделать "потом" (используя кодогенераторы из mesa)

Никто из текущих вендоров (на PC) не берет денег за OpenCL. Бери и пользуйся, никаких проблем.

У интела можно редистрибутировать вместе с программой, для GPU - это просто часть драйвера, которая уже есть.

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

Ну так это a) alfa-preview версия технологии которая b) работает в самых невыгодных для нее условиях. И все равно оно уже быстрее.

Допишут, дотюнят, и будет раз в 5 разницы, как и положено.

PS: Мне вот вообще интересно, теоретически "Photoshop" все картинки может базово держать на видеокарте, если памяти хватает, и все алгоритмы отображения и обработки туда засунуть. Тогда memcopy только при Open/Save будут ну или на гигапиксельных картинках.

PPS: они 400msec копировали 1 мегапиксель туда-обратно? Там точно есть место для улучшения.

Ну так, для начала, CPU тоже работал в неоптимальных условиях. несколько строчек (с OpenMP) ускорит это место в разы. От 3 до 4.
И для элементарных операций, вроде brightness-contrast, CPU будет всегда быстрее потому как может их хреначить со скоростью памяти, а GPU ограничена скоростью PCI-express.
Несмотря на это, выигрыш может быть (и довольно большой) за счет offloading, особенно на картах вроде Теслы с двумя DMA engine: один DMA фигачит в память карты (кусочками), карта что-то считает, второй DMA фигачит обратно. А на CPU смотрим кино с голыми бабами.

Только это сложно программировать. Куда проще делать так, как ты говоришь - загнать все в память видеокарты, прямо на ней все считать и прямо оттуда и показывать, кстати. CPU почти не нужен. Только это сложно, нужно перепрограммировать (на OpenCL/CUDA/IL) не маленькие хот-споты, а вот прямо весь код фотошопа.

Ну и касаемо GEGL. Если там по жизни тайлы 128x64, то проще наплевать и забыть. В 2011-м году, когда 4-гиговый DIMM стоит 700 рублей, тайл должен быть мегабайт на 100. Или на 1000. Или на 32, если мы на мобильном устройстве. Библиотеку, в которой такой default size для тайла, явно писали какие-то люди из музея вымерших цивилизаций.

только это сложно, нужно перепрограммировать ==> только это тоже нужно программировать (редактирования каментов у меня нет, я жадный).

Ну и раз уж пишу доп-коммент.

Мой поинт в том, что для картинок какого-то разумного размера (с монитор, 1-4 мегапикселя) GPU нахрен не нужен. Но нужно, конечно, аккуратно повыпиливать хотспоты, а не просто все на прямолинейном C запрограммировать.

Библиотеку, в которой такой default size для тайла, явно писали какие-то люди из музея вымерших цивилизаций.

Этот музей называется Rhythm & Hues :)

Я читал недавно презентацию о, пожалуй, самой эффективной реализации DGEMM на AMD GPU. Помнится, там было порядка 90% от теоретической производительности. Использовался не Compute, а Pixel шейдер. А самое интересное то, что не использовалась локальная память. Много потоков практически полностью скрывали кешированный доступ к глобальной памяти. Мне кажется, это сейчас самый энергоэффективный способ умножать матрицы.

А разве в pixel-шейдерах есть double? Я просто не в курсе...

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

Я пытаюсь донести ту мысль, что отдельное "очень быстрое умножение матриц" (или "элементарная операция с изображением") - это хорошо, но мало. Ну вот стандартный HPL, даже с довольно быстрым DGEMM оказывается ограничен в другом месте. Если бы DGEMM был бы бесконечной скорости, то весь HPL получился бы ~140GFLOP/s. Ну и с картинками: делаем буквально пяток операций, должны упереться в bandwidth памяти видеокарты (100-150Gb/sec) или, в крайнем случае, в bandwidth PCI-e (4-6Gb/sec, пусть пополам), а реальная производительность В СТО РАЗ меньше.

А чтобы получить эффективную реализацию - надо или *все* перенести на GPU (что затратно, дохрена кода придется написать, втч и такого, который на GPU работает неэффективно, скажем с ветвлениями на каждом элементе вектора/матрицы), или написать реально сложное приложение. Вот CALDGEMM, который написан для HPL-GPU: там реально дохрена напрограммировали, вроде балансирования нагрузки (Multi-)GPU/CPU и так далее. ~200kb кода на умножение матриц (а казалось бы, три вложенных цикла, 6 строчек...).

Вот, нашел: http://developer.amd.com/afds/assets/presentations/2909_1_final.pdf

Там у них своя версия HPL, кстати. И умножают они просто огромные матрицы, которые в память GPU целиком не помещаются.

А с остальными Вашими замечаниями я согласен, да.

Ну так да - там реально дохрена кода написано именно для поддержания одновременности работы всех деталей системы (CPU-GPU-DMA)

Но шейдеры там - compute. И сами шейдеры - относительно простые, от всего написанного кода DGEMM - процентов 10 кода (в байтах). А в остальных 90% творятся всякие ужасы. И драйвера патчить надо (хотя в моих тестах я пока разницы не увидел, но у меня узкое место в CPU)

> Но шейдеры там - compute

Вполне возможно. В презентации все же написано следующее: "Pixel Shader kernel and no Compute Shader"

Там, похоже, в презентации какой-то другой kernel описывается, который "максимально производительный", но зато с 'hardcoded K'

Для CAL, насколько я его понимаю, такой уж разницы между Compute и Pixel - нету. Но назвать шейдер, который гонит output обратно в память хоста (в одном из режимов) "пиксельным" я бы не смог.

И, кстати, 44-я страница презентации нереально доставляет.
- AMD Interlagos - вдвое меньше FPU (там если быть точным, можно два FPU объединить в один с 256-битными векторами), чем вообще ядер
- Ура! половину ядер (которые стали без FPU) мы будем использовать для GPU I/O

Это я все к тому же, максимальная производительность требует совершенно неестественных трюков.

А, ну да, мы про одно и то же говорим, caldgemm.

Так caldgemm не линкуется ни с одной библиотекой из SDK. Ему из SDK нужен только cal.h. А библиотеки - от драйвера. И хрен их подропаешь просто так, поломается совместимость тут же.

На OpenCL, насколько я знаю (мог что-то пропустить), разумного DGEMM пока нет.

Так дропнут саппорт в драйверах. Они ж свой новый язык готовят. А пока его нет, низкоуровнево писать не на чем.

Я тоже не видел шустрого DGEMM на OpenCL. Но я и не искал особо. Мне так кажется, написать что-то в районе 40%-50% от теоретической мощности даже я смогу. Один код для обоих производителей GPU. А 90% - это уже крайне маловероятно.

Я очень сомневаюсь, что для NV и для AMD подойдет один код и он даст "50%" от теоретической. Ну если только весь #ifdef-ами облепить.

Вот есть AMD-шный же OpenCL BLAS и FFT (без исходников), но я его не щупал за ненадобностью. А их же родной acml-gpu на их же родной 5870 50% от теоретического пика не набирает. Набирает 40.

У меня есть какой-никакой опыт отладки ядер, считающий свертки. Причем на матрицах очень небольшого размера (но их много) и с не очень большим окном. Так вот, компилятор AMD генерирует неплохой IL и, соответственно, ISA код, А компилятор NVidia генерирует PTX код еще лучше. Ну вот смотришь на него и видишь, что молодец какой, замечательно развернул циклы, сообразил, где можно из локальной памяти не читать и т.д. Получается эффективность от 20% (для AMD) до 30% (для NVidia), в среднем. А матрицы перемножать - еще проще.

acml-gpu не смотрел, взгляну.

LLVM (а он вроде у всех вендоров OpenCL на PC сейчас) - хороший компилятор.

Только вот матрицы умножать "как-то" - просто, а быстро - вылезают подробности (какой размер блока взять, что использовать под local storage).

У меня 5870 под рукой нет (засунута в другую машину, лень грузить), зато есть GTX480 и AMD-шный пример перемножения матриц из SDK. Пример - "обычный", ну там блок 4x4 и простой код. Так вот, на GTX480 у него получается ~270 GFLOP/s при том что MAGMA делает 844 (втрое больше), а peak там за терафлопс.
Это на single.

С double - сильно лучше, там оно bandwidth-limited, поэтому проседание производительности не в 8 раз, а всего в ~5. И на GTX480 получается 60 gflops (при том что у магмы - 166).

Ну то есть да, конечно, примеры пишут чтобы попроще, а не чтобы побыстрее, но вот что один и тот же код, не использующий особенности железа (где-то local storage, где-то регистры для хранения блока - это для GEMM, плюс размер блока, плюс текстурный кэш...) будет на 50% от peak для двух-трех разных железок - я верю с трудом.

Я не предлагаю отказываться от использования локальной памяти и регистров. OpenCL предоставляет достаточно возможностей для автоматического тюнинга кода. Ведь можно узнать и размер локальной памяти, и размер wavefront/warp, и максимальный размер группы, и #pragma unroll расставить.

Я сейчас скачал acml-gpu, посмотрел код. Все очень странно. Там пиксель-шейдеры, написанные на IL (#include "cal.h", ага). Один шейдер считает 4x4. Все должно очень быстро работать. Говорите, 40%? Ну очень странно. Может, кривые ручки программиста, может с драйверами проблемы (все лето AMD колбасило с драйверами).

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

А acml-gpu - ну да, ~220 GFLOP/s на 8k-матрицах (double) при том что peak - 550 или около того.
Только там дело не столько в самом коде GPU, сколько в обвязке (асинхронный трансфер, оптимальный merge результатов на CPU, прибавление 3-го аргумента тоже на CPU т.к. это линейная от данных операция и можно ее сделать прямо при трансфере). Если судить по CALDGEMM, который я не весь понял, но хотя бы частично прочитал.

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

Нет-нет, я несколько другое имею ввиду. Даже в рамках одной архитектуры для написания эффективного OpenCL кода необходимо учитывать значения некоторых параметров (размер локальной памяти, максимальный размер группы, размер wavefront/warp). ifdef-ы не нужны. Нужен код, который зависит от этих параметров. И вот имея уже такие динамически настраиваемые исходные коды OpenCL-ядер, вполне можно ожидать их адекватной производительности на схожей архитектуре. В этом смысле все чипы NVidia, AMD (в том числе и будущая архитектура Southern Island) схожи.

ifdef-ы не нужны. Нужен код, который зависит от этих параметров.

Переводя на нормальный язык:
" возможность генерировать разный код в зависимости от этих параметров не нужна. Нужен код, который зависит от этих параметров. "

P.S. http://www.gpgpu.ru/node/217#comment-1555

Из общих соображений имею возразить следущее:
1) *GEMM (и matrix multiplication) достаточно просто устроен: как правило bandwidth limited и секрет успеха - это побольше насосать в блок, чтобы за 2*K^2 чтений сделать K^3 операций (K - размер блока, считая его квадратным)
2) Блок может быть в local storage, в регистрах и (для GPU) в текстурном кэше.
3) Из параметров этой быстрой памяти нам доступен размер local storage. Ни количество регистров, ни размеры (и свойства) кэшей - не известны. А для local (shared в терминах NVidia) мы кроме размера не можем узнать другие важные свойства (такие как конфликты банков через 16 /или через 32 для Fermi/)

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

Возможно, что вышеуказанные соображения существенны, когда мы хотим шагнуть далеко за 50% от теоретического peak.

Правда вот сегодня я весь день мучал поминавшийся выше CALDGEMM на 6990 (который вовсе даже не OpenCL, а даже немножко тюнился под HD6xxx, хотя основная платфрма у авторов - HD5870) и получил 62% от теоретического пика на одном GPU и чуть больше 50% на четырех GPU (2x 6990).

Может Вы и правы, и я со своими заявлениями о переносимости производительности в 40%-50% несколько оптимистичен :)

Вот сорока принесла на хвосте свежую статью (4-й автор - Донгарра) про реализацию *SYMV в MAGMA 0.2/0.3

Никакой мультиплатформенности, ну кроме Fermi/не ферми, однако
"наша реализация SSYMV в 7 раз быстрее чем CUBLAS4 на GTX280 и в 4.5 раза быстрее на Тесле, наша DSYMV в 2 раза быстрее чем CUBLAS4 на тесле"

А ведь CUBLAS, по идее, тоже не левой ногой делалась, это вендорская библиотека для вендорского оборудования, если она будет неэффективной, то оборудования меньше купят.

Но ведь это не GEMM. Тут ведь необходимо приложить минимальные умственные усилия (удивительно, что люди пишут научные статьи на эту тему), чтобы начать таргетить (прошу прощения) теоретическую производительность. Но за ссылку на статью спасибо.

Как раз "научность" статей не удивляет. Изучаются свойства материального объекта, созданного человеком - ну и что? Какой-нть условный "криптоанализ хэшей MD5" - очевидно что прекрасная (очень научная) тема научной работы, а объект для изучения тоже создан человеком и при этом нематериален.

А по поводу "минимальных умственных усилий" я абсолютно не согласен. Если *GEMM реализовывать "минимальными умственными усилиями" (для CUDA), то получится как выше процитировано (270 при возможных 844 и теоретических 1344). И нужны свежие дебютные идеи, вроде тех что у Волкова (все на регистрах, плюем на occupancy). Они, наверное, несколько более прямолинейные, чем для SYMV, но все едино нужны.

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

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

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

Не, ну понятно, что 95% статей "про алгоритмы" - это "крутили гайку номер 6 ключом на 18". Новые алгоритмы появляются нечасто, а большинство вполне серьезных и "настоящих" работ - это компоновка решения из готовых кусков.

Ну та и 95% научных работ - они такие. То что мы называем исследователей природы (комбинирующих известные методы) "учеными", а исследователей компьютерного железа хочется назвать "инженерами" - это дело привычки.

Ахаха, да, может я и идеализирую "настоящих" ученых :)

Дропать нельзя, сломаются юзерские приложения.

Да, соглашусь. Но через годик легко уже смогут прекратить поддерживать CAL уже в драйверах.

Я не думаю что так быстро, все-таки оно как-то распространено.

Мое предсказание: только с полной сменой архитектуры, так что CAL придется эмулировать и оттого станет медленно.

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

Nakasato? Прочитал, в pixel-шейдерах double есть.

Как я понимаю, основная проблема с этим подходом в том, что входные данные - это текстура, а значит размер получается сильно ограниченным.

Вот тут интервью австралийской фирмы Euclideon, которые смогли на современном уровне сделать воксельную графику, которую лет 10 назад похоронили. Вам подробности не попадались?