Хабраиндекс
402,15
21 декабря 2012 в 16:27

Android: Написание многопоточных приложений с помощью Intel® Threading Building Blocks tutorial

Совсем недавно мы рассматривали написание многопоточных приложений для магазина Windows с помощью Intel® Threading Building Blocks(Intel® TBB). Там утверждается, что использование кроссплатформенной библиотеки TBB позволяет легко переносить вычислительную часть на другие платформы. Android как раз сгодится для хорошего примера одной из «других платформ», подробности под катом.

В недавно вышедшем релизе библиотеки Intel® TBB tbb41_20121112oss, который доступен для загрузки на нашем сайте threadingbuildingblocks.org, добавлена экспериментальная поддержка приложений для Android, т.е. построение нативных библиотек для использования Android приложениями через JNI интерфейс.

Для построения библиотеки будем использовать Android SDK Tools Rev.21 и Android NDK Rev 8C. Но тут необходимо оговориться, что, как оказалось при подготовке материала, сказалась экспериментальность и построение самих библиотек в этом релизе. «Из коробки» работает только под NDK на Linux. Мы поправим это в одном из следующих обновлений. Я подпилил немного makefile для Windows и построил библиотеку там, но для простоты будем подразумевать, что сама библиотека была построена на Linux, а всё остальное сделано на Windows. В общем, налицо еще одно преимущество кроссплатформенной библиотеки.

Итак, что же необходимо сделать, чтобы собрать простое приложение с использованием Intel TBB.
Для начала необходимо распаковать и откомпилировать библиотеку, поскольку этот релиз распространяется только в исходниках. Подразумевается, что есть gnu make, в командной строке с выставленным окружением для NDK выполняем команду

gmake tbb tbbmalloc target=android

Со стороны библиотеки всё, переходим в Eclipse. При помощи шаблонов создаем простое приложение и, для простоты, как в предыдущем случае назовём его app1:


Activity выберем FullscreenActivity. На этом работа шаблона завершится. Обратите внимание, что com.example* приложения не приветствуются магазином гугл. Но для примера вполне сойдет.

Затем добавим пару кнопок на основной фрейм. После этого XML файл основного фрейма (app1/res/layout/activity_fullscreen.xml) будет выглядеть примерно так

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0099cc"
    tools:context=".FullscreenActivity" >
    <TextView
        android:id="@+id/fullscreen_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:keepScreenOn="true"
        android:text="@string/dummy_content"
        android:textColor="#33b5e5"
        android:textSize="50sp"
        android:textStyle="bold" />
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true" >
        <LinearLayout
            android:id="@+id/fullscreen_content_controls"
            style="?buttonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="74dp"
            android:layout_gravity="bottom|center_horizontal"
            android:background="@color/black_overlay"
            android:orientation="horizontal"
            tools:ignore="UselessParent" >
            <Button
                android:id="@+id/dummy_button1"
                style="?buttonBarButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/dummy_button1"
                android:onClick="onClickSR" />
            <Button
                android:id="@+id/dummy_button2"
                style="?buttonBarButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/dummy_button2"
                android:onClick="onClickDR" />
        </LinearLayout>
    </FrameLayout>
</FrameLayout>

А файл со строками (app1/res/values/strings.xml) будет выглядеть так:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Sample</string>
    <string name="dummy_content">Reduce sample</string>
    <string name="dummy_button1">Simple Reduce</string>
    <string name="dummy_button2">Deterministic Reduce</string>
</resources>

Так же добавим обработчики кнопок
// JNI functions
private native float onClickDRCall();
private native float onClickSRCall();
    
	public void onClickDR(View myView) {
		TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content)); 
		float res=onClickDRCall();
		tv.setText("Result DR is \n" + res);
	}

	public void onClickSR(View myView) {
		TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content)); 
		float res=onClickSRCall();
		tv.setText("Result SR is \n" + res);
	}



И загрузку библиотек в FullscreenActivity.java
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

…
		System.loadLibrary("tbb");
		System.loadLibrary("jni-engine");		
	}

Если с библиотекой «tbb» должно быть всё понятно, то на «jni-engine» нужно остановиться подробнее.

«jni-engine» — это С++ библиотека, которая реализует вычислительную часть и выставляет С-интерфейсы для JNI вызовов onClickSRCall() и onClickDRCall().

Согласно правилам разработки внутри проекта создаем каталог ‘jni’ и создаем там 3 файла, специфичные для нашей библиотеки «jni-engine». Это:
Android.mk (в <> скобках данные, в которые необходимо подставить актуальные значения)
LOCAL_PATH := $(call my-dir)
TBB_PATH := <path_to_the_package>
	
include $(CLEAR_VARS)

LOCAL_MODULE    := jni-engine
LOCAL_SRC_FILES := jni-engine.cpp 
LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -I$(TBB_PATH)/include
LOCAL_LDLIBS := -ltbb -L./ -L$(TBB_PATH)/<path_to_libtbb_so>

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := libtbb
LOCAL_SRC_FILES := libtbb.so
include $(PREBUILT_SHARED_LIBRARY)

Application.mk
APP_ABI := x86
APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti
APP_STL := system

jni-engine.cpp:
#include <jni.h>

#include "tbb/parallel_reduce.h"
#include "tbb/blocked_range.h"

float SR_Click()
{
    int N=10000000;
    float fr = 1.0f/(float)N;
    float sum = tbb::parallel_reduce(
        tbb::blocked_range<int>(0,N), 0.0f,
        [=](const tbb::blocked_range<int>& r, float sum)->float {
            for( int i=r.begin(); i!=r.end(); ++i )
                sum += fr;
            return sum;
    },
        []( float x, float y )->float {
            return x+y;
    }
    );
    return sum;	
}

float DR_Click()
{
    int N=10000000;
    float fr = 1.0f/(float)N;
    float sum = tbb::parallel_deterministic_reduce(
        tbb::blocked_range<int>(0,N), 0.0f,
        [=](const tbb::blocked_range<int>& r, float sum)->float {
            for( int i=r.begin(); i!=r.end(); ++i )
                sum += fr;
            return sum;
    },
        []( float x, float y )->float {
            return x+y;
    }
    );	
    return sum;	
}

extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickDRCall(JNIEnv *env, jobject obj)
{
    return DR_Click();
}

extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickSRCall(JNIEnv *env, jobject obj)
{
    return SR_Click();
}

Как видно, используем те же алгоритмы, что и в предыдущем блоге.

При построении с помощью NDK он сам раскладывает библиотеки в нужные каталоги, в том числе libjni-engine.so и libtbb.so.

Теперь переходим обратно в eclipse и строим .apk файл. Приложение готово для установки на AVD или на устройство. Вот так это выглядит на AVD:



Вот и всё! Наше простое приложение написано! А для тех, кто использовал код предыдущего блога, приложение успешно перенесено на Android.

Для тех, кто заинтересовался, пробуйте:

Скачать библиотеку Intel® Threading Building Blocks (Версия с открытым исходным кодом):
threadingbuildingblocks.org
Коммерческая версия Intel® TBB (функционально не отличается):
software.intel.com/en-us/intel-tbb

Англоязычные и русскоязычные блоги об Intel® TBB
software.intel.com/en-us/tags/17207
software.intel.com/en-us/tags/17220
Ну и, конечно, наш форум,
software.intel.com/en-us/forums/intel-threading-building-blocks
+24
6824
104
vpolin 19,3

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

0
DmitryO, #
До чего техника дошла! (С)
Ну и ожидаемый вопрос — ты ведь наверняка попробовал и на настоящей железке. Как оно, заработало? ;)
+1
michaelarshinov, #
отвечу за автора — да, работает прекрасно!
0
michaelarshinov, #
кстати говоря разрешите полюбопытствовать :) почему Вы сомневаетесь что на реальном устройстве приложение не запускается, или еще чего?
0
DmitryO, #
Зная автора лично, я, скорее, не сомневаюсь, а немного удивляюсь: как же он удержался и не привел пару цифр сравнения произволительности с последовательной версией ;)
0
michaelarshinov, #
звучит здорово! возможно Вы бы помогли с этим :)
0
vpolin, #
Я сдержусь:-)

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

Кстати, для начала вот интересная статья, которая охватывает также Intel TBB на Android.

--Владимир
0
vpolin, #
«Заработало» оно еще полтора года назад у наших пользователей, разработчиков библиотеки OpenCV. Но между «заработало» и продуктизацией может пройти достаточно большой промежуток времени:-)
0
melnichek, #
А под arm она работает?
+1
vpolin, #
Статья, чуть выше по комментариям, как раз рассказывает про работу на Tegra 2/3.
Предваряя аналогичный вопрос про работу на MIPS — я пока ничего не слышал.

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