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

в 5:45, , рубрики: Блог компании Intel, Разработка под android

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 и сделаем соответствующие замеры времени монохромной обработки изображений. Результаты представлены ниже на графике и в таблице.
Использование Renderscript на android устройствах с процессорами Intel®

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 может стать помощником повышения производительности приложений, которые при этом останутся аппаратно-независимыми.

Автор: rkazantsev

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js