
Я до сих пор помню свой первый визит в Лувр, когда я стоял как завороженный перед поразительно темной картиной в контрастно яркой золотой раме. Каждый мазок кисти был приглашением окунуться в мир внутри холста, и чем больше я смотрел, тем больше это открывалось мне. И с тех пор я являюсь поклонником Pandemonium Джона Мартина.
Вы не поверите, но графические процессоры существуют с 1970-х годов. Ранние графические процессоры были устройствами, ориентированными исключительно на доставку графики и работающими независимо от процессора. Хотя значение аббревиатуры осталось неизменным, возможности и приложения радикально изменились. Современные графические процессоры - это мощные системы с высокой пропускной способностью, позволяющие им передавать реалистичную графику со сверхвысоким разрешением и высокой частотой кадров. Графические процессоры эволюционировали для размещения сотен ядер, способных параллельно обрабатывать большие объемы инструкций. Эта способность к параллельному процессу привлекла внимание исследователей из разных областей. Сегодня графические процессоры используются во множестве приложений, от машинного обучения, хранилищ потоковых данных, аналитических баз данных до Java.
За прошедшие годы появилось несколько проектов, позволяющих выполнять собственный Java-код на графических процессорах. Хорошо, поэтому возможность переноса операций, связанных с процессором, из Java и создания их в виде библиотек с использованием OpenCL, была и всегда была возможной. Однако это намного интереснее. Речь идет о запуске собственного кода Java непосредственно на гетерогенных устройствах с минимальными изменениями кода. Я разработал план, чтобы проверить это. Теперь, если бы я просто написал приложение, которое просматривает чрезвычайно длинный массив и выполняет вычисления для каждого элемента, я мог бы с легкостью протестировать конкурентов. Но где в этом веселье?

Если вам интересно, о каком дьяволе я веду все эти разговоры о Pandemonium, у меня есть хитрый план. В заголовке этой статьи закодировано скрытое сообщение. Его не видно невооруженным глазом, но если вы знаете, как и где смотреть, вы его найдете. Это стеганография. Чтобы быть конкретным, я использовал здесь стеганографию LSB (младший значащий бит). Теория довольно проста. Позволь мне объяснить.
Изображение - это, по сути, массив пикселей. Что мы собираемся сделать, так это взять пиксель, преобразовать его в биты, а затем установить младший бит пикселя. Изменение фактического цвета пикселя, которое это вызовет, настолько незначительно, что его не будет видно невооруженным глазом.

Теперь давайте сосредоточимся на том, как мы можем скрыть сообщение (или даже файл) внутри изображения. Предположим, нам нужно скрыть цифру пять на изображении. 5 в двоичном формате - это 101. Каждый бит сообщения будет использовать младший бит пикселя изображения.

Если вы внимательно следите, то поймете, что для каждого кодируемого байта требуется восемь пикселей изображения. Чем длиннее сообщение, тем больше операций с массивом вам придется выполнить. Чтобы было немного интереснее, я собираюсь создать шифр длины сообщения на основе хэша пароля. Затем я собираюсь объединить шифр и сообщение, чтобы создать зашифрованный текст, который будет скрыт на изображении.
Давайте на секунду изменим наше восприятие. Когда мы думаем о массивах, мы думаем о чем-то, что нужно обрабатывать последовательно. Что, если мы посмотрим на это как на набор отдельных вычислений, которые можно выполнять параллельно. Это то, что сделал бы графический процессор, вычислив каждый элемент массива одновременно. Чтобы получить ожидаемый выигрыш в производительности, мне нужно выполнить только часть моего приложения на графическом процессоре. Видите ли, каким бы мощным ни был графический процессор, это не мастер на все руки, это работа центрального процессора.
Здесь вам пригодится возможность выполнять ваше приложение на разнородном оборудовании. Первый шаг - понять, какие элементы моего приложения выиграют от использования графического процессора. Затем я мог бы переписать свой код так, чтобы часть моего кода выполнялась с использованием графического процессора, а остальная часть - на процессоре. Когда я реструктурирую свой код, мне нужно знать, что я не использую сложные типы данных и не обращаюсь к методам блокировки, которые противоречат цели параллельной обработки. Я буду использовать для этого aparapi ¹. Вышеупомянутый цикл FOR будет выглядеть так при написании с использованием aparapi.
Когда тестовое приложение и тестовая установка были готовы, настало время для тестирования производительности! Я использовал экземпляр AWS g4dn.xlarge с nVidia Tesla T4 GPU и четырьмя процессорами 2-го поколения Intel Xeon vCPU. Результаты совсем не удивили.

Глядя на графики, мы можем увидеть среднее улучшение на 25% с помощью графического процессора. Приложение стеганографии занимает около 1000 миллисекунд из-за длинных массивов, связанных с размером изображения и длиной сообщения. На самом деле мы только задействуем возможности графического процессора для параллельных процессов. Еще один аспект, в котором графический процессор превосходит центральный процессор, - это когда вам нужно выполнять сложные математические операции.
Подумайте о сценарии клеточной триангуляции. Оператор мобильной связи отслеживает пользователя по вышкам сотовой связи. Пользователь C был зарегистрирован на вышках A и B. Оператор мобильной связи имеет информацию о том, как далеко находится клиент от каждой вышки сотовой связи, а также о расстоянии между двумя вышками сотовой связи. Используя эту информацию, мы можем вычислить, под каким углом находится пользователь к вышке сотовой связи B. Затем, используя угол и географические координаты вышки B, мы можем вычислить координаты клиента.

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

Мораль истории? Что ж, если ваше приложение состоит из сложных манипуляций с массивами или математических вычислений, которые могут выполняться параллельно, то выполнение части вашего кода на графическом процессоре имеет смысл. Однако есть компромиссы. Графические процессоры плохо справляются с выполнением операций на основе переключателей. Если в вашей логике требуются операторы IF-ELSE или SWITCH-CASE, то лучше использовать ЦП. Еще один недостаток - необходимость реструктуризации кода. Есть такие проекты, как TornadoVM², которые позволяют минимизировать реструктуризацию кода. Вместо этого код, который будет выполняться на графическом процессоре, можно пометить с помощью аннотаций. Тем не менее, незначительные изменения кода и способа, которые вы считаете необходимыми, поскольку эти библиотеки не работают со сложными типами данных, то есть String не будут работать.
Хорошо, я согласен, что эта статья была немного информационным попурри. Причина заключалась в том, что я пытался до некоторой степени охватить концепции, используемые в этой статье, но не для того, чтобы утомлять читателей живым дневным светом. Если вам нужна дополнительная информация по любой затронутой здесь теме, пожалуйста, свяжитесь с нами. Если вам интересно опробовать примеры, использованные в этой статье, я связал репозиторий git⁵ здесь.
А если вам интересно узнать, какие секреты предлагает изображение заголовка, пароль - потерянный рай.
Перед ними был весь мир, где выбрать
место их отдыха, а провидение - их проводник:
Они, взявшись за руки, блуждающими и медленными шагами,
прошли одинокий путь через Эдем .
[1] https://github.com/Syncleus/aparapi
[2] https://github.com/beehive-lab/TornadoVM
[3] https://en.wikipedia.org/wiki/Steganography
[4] https://en.wikipedia.org/wiki/Mobile_phone_tracking#Network-based
[5] https://github.com/charith26/SteganographyGPU
[6] https://commons.wikimedia.org/wiki/File:John_Martin_Le_Pandemonium_Louvre.JPG