Хабраиндекс
319,12
29 ноября 2012 в 09:45

Использование Renderscript на android-устройствах с процессорами Intel®

image

В статье я хотел бы дать краткое описание работы технологии Renderscript внутри Android, сравнить ее производительность с Dalvik на конкретном примере аndroid-устройства с процессором Intel и рассмотреть небольшой прием оптимизации renderscript.
Renderscript – это API, который включает функции для 2D/3D рендеринга и математических вычислений с высокой производительностью. Он позволяет описать какую-либо задачу с однотипными независимыми вычислениями над большим объемом данных и разбить ее на однородные подзадачи, которые могут быть выполнены быстро и параллельно на многоядерных Android-платформах.
Такая технология может повысить производительность ряда dalvik приложений, связанных с обработкой изображений, распознаванием образов, физическим моделированием, клеточно-автоматной моделью и др., которые, в свою очередь, не потеряют аппаратной независимости.

1. Технология Renderscript внутри Android


Приведу краткий обзор механизма работы технологии Renderscript внутри Android, ее достоинства и недостатки.

1.1 Renderscript offline-компиляция

Renderscript начал поддерживаться в Honeycomb/Android 3.0 (API 11). А именно, в Android SDK в директории platform-tools появился llvm-rs-cc (offline compiler) для компиляции renderscript (*.rs файл) в байт-код (*.bc файл) и генерации java классов объектов (*.java файлы) для структур, глобальных переменных внутри renderscript и самого renderscript. В основе llvm-rs-cc лежит Clang компилятор с небольшими изменениями под Android, который представляет собой front-end для LLVM компилятора.
image

1.2 Renderscript run-time компиляция

В Android появился framework, построенный на базе LLVM back-end, который отвечает за run-time компиляцию байт-кода, линковку с нужными библиотеками, запуск и контроль выполнения renderscript. Этот framework состоит из следующих частей: libbcc занимается инициализацией LLVM контекста в соответствии с указанными прагмами и другими метаданными в байт-коде, компиляцией байт-кода и динамической линковкой с нужными библиотеками из libRS; libRS содержит реализацию библиотек (math, time, drawing, ref-counting,…), структур и типов данных (Script, Type, Element, Allocation, Mesh, various matrices,…).

image
Преимущества:
  • Аппаратно-независимое приложение получается за счет того, что renderscript байт-код, входящий в apk файл, в run-time будет скомпилирован в машинный код того аппаратно-вычислительного модуля (CPU) платформы, где будет запущен;
  • Быстрота исполнения достигается благодаря распараллеливанию вычислений, run-time компиляторной оптимизации и нативному исполнению кода.

Недостатки:
  • Отсутствие подробной документации для работы с renderscript усложняет разработку приложений. Все ограничивается коротким описанием предлагаемого renderscript run-time API, представленного здесь;
  • Отсутствие поддержки исполнения renderscript потоков на GPU, DSP. Возможны проблемы с run-time балансировкой потоков в гетерогенном запуске, управлением общей памятью.

2. Dalvik vs. Renderscript в монохромной обработке изображения


Рассмотрим dalvik-функцию Dalvik_MonoChromeFilter (преобразование цветного RGB-изображения в черно-белое (монохромное) ):

private void Dalvik_MonoChromeFilter() {
    	float MonoMult[] = {0.299f, 0.587f, 0.114f};
    	int mInPixels[] = new int[mBitmapIn.getHeight() * mBitmapIn.getWidth()];
    	int mOutPixels[] = new int[mBitmapOut.getHeight() * mBitmapOut.getWidth()];
    	mBitmapIn.getPixels(mInPixels, 0, mBitmapIn.getWidth(), 0, 0,
    			mBitmapIn.getWidth(), mBitmapIn.getHeight());
    	for(int i = 0;i < mInPixels.length;i++) {
    		float r = (float)(mInPixels[i] & 0xff);
    		float g = (float)((mInPixels[i] >> 8) & 0xff);
    		float b = (float)((mInPixels[i] >> 16) & 0xff);

    		int mono = (int)(r * MonoMult[0] + g * MonoMult[1] + b * MonoMult[2]);

    		mOutPixels[i] = mono + (mono << 8) + (mono << 16) + (mInPixels[i] & 0xff000000);
    	}
    	mBitmapOut.setPixels(mOutPixels, 0, mBitmapOut.getWidth(), 0, 0,
    			mBitmapOut.getWidth(), mBitmapOut.getHeight());
}

Что можно сказать? Простой цикл с независимыми итерациями, «перемалывающий» кучу пикселов. Посмотрим, как быстро он работает!
Для эксперимента возьмем МегаФон Mint на Intel® Atom™ Z2460 1.6GHz с Android ICS 4.0.4 и 600x1024 картинку с лего-роботом, несущим новогодние подарки.

image image

Замеры затраченного времени на обработку будем делать по следующей схеме:

private long startnow;
private long endnow;

startnow = android.os.SystemClock.uptimeMillis();
Dalvik_MonoChromeFilter();
endnow = android.os.SystemClock.uptimeMillis();
Log.d("Timing", "Exеcution time: "+(endnow-startnow)+" ms");

Сообщение с тегом «Timing» можно получить с помощью ADB. Сделаем десяток замеров, перед каждым из которых сделаем перезагрузку устройства и убедимся, что разброс результатов измерений небольшой.
Время обработки изображения dalvik-реализацией составило 353 мсек.
Замечание: используя средства многопоточности (к примеру, класс AsyncTask для описания задач, выполняющихся в отдельных потоках), в лучшем случае можно выжать двухкратное ускорение, в силу наличия двух логических ядер на Intel Atom Z2460 1.6GHz.
Теперь рассмотрим renderscript-реализацию RS_MonoChromeFilter того же самого фильтра:

//mono.rs
//or our small renderscript
#pragma version(1)
#pragma rs java_package_name(com.example.hellocompute)

//multipliers to convert a RGB colors to black and white
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};

void root(const uchar4 *v_in, uchar4 *v_out) {
  //unpack a color to a float4
  float4 f4 = rsUnpackColor8888(*v_in);
  //take the dot product of the color and the multiplier
  float3 mono = dot(f4.rgb, gMonoMult);
  //repack the float to a color
  *v_out = rsPackColorTo8888(mono);
}

    private RenderScript mRS;
    private Allocation mInAllocation;
    private Allocation mOutAllocation;
    private ScriptC_mono mScript;
    …
    private void RS_MonoChromeFilter() {
        mRS = RenderScript.create(this);/*создание Renderscript-контекста*/
        mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
            Allocation.MipmapControl.MIPMAP_NONE,
            Allocation.USAGE_SCRIPT);/*выделение и инициализация общей памяти для dalvik и renderscript контекстов */
        mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());
        mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);/*создание и
        привязка renderscript к renderscript-контексту */ 
        mScript.forEach_root(mInAllocation, mOutAllocation);/*вызываем renderscript-функцию root c SMP параллелизмом в 2 потока */
        mOutAllocation.copyTo(mBitmapOut);
    }


Замечание: производительность реализации будем оценивать как для dalvik.
Время обработки того же изображения renderscript-реализацией составило 112 мсек.
Получили выигрыш в производительности равный 3.2x (сравнение времени работы dalvik и renderscript: 353/112 = 3,2).
Замечание: время работы renderscript-реализации включает создание renderscript-контекста, выделение и инициализацию необходимой памяти, создание и привязку renderscript к контексту и работу функции root в mono.rs.
Замечание: Критичным местом для разработчиков мобильных приложений является размер получаемого apk файла. В этой реализации размер apk файла может увеличиться только на размер renderscript в байт-коде (*.bc файл) по сравнению с dalvik-реализацией. В моем случае размер dalvik-версии был равен 404KB, а размер renderscript-версии стал равен 406KB, из которых 2KB это renderscript байт-код (mono.bc).

3. Оптимизация renderscript


Текущую производительность renderscript можно повысить, отказавшись немного от точности арифметических операций с вещественными числами, что непринципиально для рассматриваемой задачи. Для этого в renderscript добавим прагму rs_fp_imprecise:

//mono.rs
//or our small renderscript
#pragma version(1)
#pragma rs java_package_name(com.example.hellocompute)
#pragma rs_fp_imprecise
//multipliers to convert a RGB colors to black and white
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};

void root(const uchar4 *v_in, uchar4 *v_out) {
  //unpack a color to a float4
  float4 f4 = rsUnpackColor8888(*v_in);
  //take the dot product of the color and the multiplier
  float3 mono = dot(f4.rgb, gMonoMult);
  //repack the float to a color
  *v_out = rsPackColorTo8888(mono);
}

Как следствие этого, получаем дополнительный 10%ный прирост производительности у renderscript-реализации: 112 мсек. -> 99 мсек.
Замечание: в результате получаем визуально такое же монохромное изображение без каких-либо артефактов и искажений.
Замечание: У renderscript отсутствует механизм явного управления run-time компиляторной оптимизацией в отличие от NDK, т.к. компиляторные ключи предварительно прописаны внутри Android для каждой платформы (x86, ARM,…).

4. Зависимость времени работы dalvik и renderscript реализаций от размеров изображений


Исследуем следующий вопрос: какая зависимость времени работы каждой реализации от размера обрабатываемого изображения? Для этого возьмем 4 изображения размерами 300x512, 600x1024 (наше исходное изображение с лего-роботом), 1200x1024, 1200x2048 и сделаем соответствующие замеры времени монохромной обработки изображений. Результаты представлены ниже на графике и в таблице.

300x512 600x1024 1200x1024 1200x2048
dalvik 85 353 744 1411
renderscript 75 99 108 227
выигрыш 1.13 3.56 6.8 6.2

Заметим линейную зависимость времени для dalvik относительно размера изображения в отличие от renderscript. Это отличие можно объяснить наличием времени инициализации renderscript-контекста.
Для изображений сравнительно малых размеров выигрыш несущественный, т.к. время инициализации renderscript-контекста около 50-60 мсек. Однако на изображениях средних размеров, которые чаще всего используются на android-устройствах, выигрыш составляет 4-6x.

Заключение


В статье были рассмотрены dalvik и renderscript реализации монохромной обработки изображений разных размеров. За счет распараллеливания, компиляторной оптимизации и нативного исполнения кода renderscript солидно превосходит dalvik в производительности для изображений средних размеров. Этим небольшим примером я старался показать, когда renderscript может стать помощником повышения производительности приложений, которые при этом останутся аппаратно-независимыми.

+18
6078
39
Похожие публикации

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

+4
Dreddik #
Это все хорошо, но это только для Atom.
Логичнее было бы сравнивать в этом контексте RenderScript и OpenGL
0
AterCattus #
Как-то я не уверен, что разовое выполнение пиксельного шейдера на небольшой картинке выйдет быстрее. Сюда же не только время самой обработки относится, но и инициализация контекста и шейдера, загрузка текстуры, обработка и выгрузка обратно в оперативку.
Для массовой обработки уже получше, но тут уже вопрос скорости обмена по шине.
0
rkazantsev #
Согласен сравнить RenderScript и OpenGL ES не помешало бы, но фаворитом я считаю OpenGL. Поэтому стоит подождать поддержки GPU renderscript технологией, тогда эта дуэль станет еще интереснее.
В блоге android-developers.blogspot.com/2012/01/levels-in-renderscript.htm получили хорошие результаты для гамма-коррекции на других платформах:
800x423 image
Device Dalvik RS Gain
Xoom 174ms 39ms 4.5x
Galaxy Nexus 139ms 30ms 4.6x
Tegra 30 device 136ms 19ms 7.2x
800x423 image with gamma correction
Device Dalvik RS Gain
Xoom 994ms 259ms 3.8x
Galaxy Nexus 787ms 213ms 3.7x
Tegra 30 device 783ms 104ms 7.5x
+3
gildor #
<geek-mode>
Это не «картинка с лего-роботом», а «картинка с лего-штурмовиком»
</geek-mode>
0
Tairesh #
Это же клон, а не робот!

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