Многие Android-устройства используют процессоры с несколькими вычислительными ядрами, поэтому в отрасли разработки мобильных приложений всё более важным становится умение создавать многопоточные программы. Компания Intel предлагает ценный инструментарий для разработки «параллельных» приложений – он называется Intel Threading Building Blocks (Intel TBB). По существу, Intel TBB представляет собой кросс-платформенную библиотеку шаблонов для создания параллельных программ. Она позволяет создавать и синхронизировать потоки данных, оставляя за кадром детали архитектуры и позволяя вам работать на высоком уровне абстрагирования. Intel TBB поддерживает все архитектуры. Что касается ОС Android, то следует использовать версию 4.3 и выше.
Для вычисления Пи можно выбрать в Wikipedia любую формулу с определенным интегралом. Я выбрал следующую формулу:
Для данной программы я модифицировал эту формулу:
Для вычисления интеграла я использовал метод прямоугольников. Интегрируемая функция разбивается на N = 107 равных подинтервалов длиной h = 2·10-7. Затем вычисляется аппроксимация интеграла – сложением площадей (основание х высоту) N прямоугольников по формуле:
Создадим новое Android-приложение:
Создадим «основную активность» (Main Activity). Копируем следующий код в res/layout/activity_main.xml:
А в res/values/strings.xml такой код:
Теперь Main Activity выглядит примерно так:
Далее нам нужно реализовать для своей активности интерфейс Java. В файл src/intel.example.pitbbcalc/MainActivity.java добавим следующий код:
В Project Explorer -> Android Tools -> Add Native Support можно задать нативную поддержку нажатия правой кнопкой на нашем проекте. В следующем окне вводим имя библиотеки нашего проекта и щёлкаем мышью на Finish.
Итак, мы реализовали нативный код для вычисления интеграла в одном потоке. В файл jni/PiTBBCalc.cpp добавим следующий код:
Попробуем запустить наше приложение и вычислить число Пи в однопоточном режиме:
Чтобы с помощью Intel TBB добавить в проект параллельные вычисления, нужно отредактировать jni/Android.mk. Android.mk – это Makefile для нашего проекта, и нужно добавить в него библиотеки Intel TBB:
В директории jni/ создадим файл Application.mk и добавим в него следующие строки:
Здесь назначение Application.mk состоит в описании необходимых нашему приложению нативных модулей (то есть статический/общих библиотек). В строке APP_ABI := x86_64 зададим нашу целевую архитектуру.
Теперь попробуем запустить приложение. Если вы увидите основной экран приложения, значит библиотеки Intel TBB слинковались успешно, и мы можем начинать разработку нашего приложения.
Для добавления параллелизма нам следует включить заголовок Intel TBB: #include «tbb/tbb.h», удалить цикл и добавить следующий код:
И теперь, если запустить приложение, оно будет работать в многопоточном режиме.
Как видите, разработка параллельных приложений очень проста. Используя в качестве примера вычисление числа Пи, мы смогли успешно продемонстрировать, как концепции однопоточного кода применяются в многопоточном коде.
Хозяйке на заметку:
» Threading Building Blocks – базовые элементы многопоточности
» Создание параллельных Android-приложений для 64-разрядной архитектуры с использованием Intel TBB
» Руководство по Android: написание многопоточных приложений с помощью Intel Threading Building Blocks
Создание Intel TBB
- Скачайте Intel TBB, найти можно здесь: www.threadingbuildingblocks.org.
- Добавьте NDK в PATH. Для Windows:
$ SET PATH=%PATH%;
Для Linux:
$ export PATH=$PATH:
- Распакуйте TBB и перейдите в директорию с исходным кодом и в папку src.
$ cd /src/
- Запустите TBB для Android:
$ /ndk-build –C /src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc –j
Эта команда создает TBB для 64-разрядной версии Android 64. Чтобы сформировать TBB для 32-разрядной версии Android, замените arch=intel64 на arch=ia32.
- Библиотека создана. В соответствующей директории (/build/) вы найдете поддиректории с библиотеками: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so и libgnustl_shared. Теперь их можно использовать в нашем приложении.
Вычисление числа Пи
Для вычисления Пи можно выбрать в Wikipedia любую формулу с определенным интегралом. Я выбрал следующую формулу:
Для данной программы я модифицировал эту формулу:
Для вычисления интеграла я использовал метод прямоугольников. Интегрируемая функция разбивается на N = 107 равных подинтервалов длиной h = 2·10-7. Затем вычисляется аппроксимация интеграла – сложением площадей (основание х высоту) N прямоугольников по формуле:
Создание приложения
Создадим новое Android-приложение:
Создадим «основную активность» (Main Activity). Копируем следующий код в res/layout/activity_main.xml:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="intel.example.pitbbcalc.MainActivity" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/pi_equally"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pi_equally"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/pi_val"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
А в res/values/strings.xml такой код:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PiTBBCalc</string>
<string name="action_settings">Settings</string>
<string name="start">Start</string>
<string name="title">Calculation of π</string>
<string name="pi_equally">π = </string>
</resources>
Теперь Main Activity выглядит примерно так:
Далее нам нужно реализовать для своей активности интерфейс Java. В файл src/intel.example.pitbbcalc/MainActivity.java добавим следующий код:
package intel.example.pitbbcalc;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private native double onClickCalc();
private TextView piEqually;
private TextView piVal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startButton = (Button) findViewById(R.id.startButton);
piEqually = (TextView) findViewById(R.id.pi_equally);
piVal = (TextView) findViewById(R.id.pi_val);
piEqually.setVisibility(View.INVISIBLE);
piVal.setVisibility(View.INVISIBLE);
startButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO автоматически генерируемая заглушка
double val = onClickCalc();
piVal.setText(String.valueOf(val));
piEqually.setVisibility(View.VISIBLE);
piVal.setVisibility(View.VISIBLE);
}
});
System.loadLibrary("PiTBBCalc");
System.loadLibrary("tbb");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// заполнение меню; добавление элементов в строку действий, если есть.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Обработка элементов строки действий. Действия будут
// автоматически обрабатывать щелчки мышью на Home/Up, если
// в AndroidManifest.xml задается родительское действие.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
В Project Explorer -> Android Tools -> Add Native Support можно задать нативную поддержку нажатия правой кнопкой на нашем проекте. В следующем окне вводим имя библиотеки нашего проекта и щёлкаем мышью на Finish.
Итак, мы реализовали нативный код для вычисления интеграла в одном потоке. В файл jni/PiTBBCalc.cpp добавим следующий код:
#include <jni.h>
#include <math.h>
double piIntFunc (const double x)
{
return sqrt(1 - pow(x, 2.0));
}
double calcPi()
{
const unsigned int n = pow(10.0, 7);
double a(-1), b(1);
double h = (b - a) / n;
double x (a);
for (unsigned int i (0); i < n; ++i)
{
sum += piIntFunc(x);
x += h;
}
sum *= h;
return 2 * sum;
}
extern "C"
JNIEXPORT jdouble JNICALL Java_intel_example_pitbbcalc_MainActivity_onClickCalc(JNIEnv *env,
jobject obj)
{
return calcPi();
}
Попробуем запустить наше приложение и вычислить число Пи в однопоточном режиме:
Чтобы с помощью Intel TBB добавить в проект параллельные вычисления, нужно отредактировать jni/Android.mk. Android.mk – это Makefile для нашего проекта, и нужно добавить в него библиотеки Intel TBB:
LOCAL_PATH := $(call my-dir)
TBB_PATH := <tbb_sources>
TBB_BUILD_PATH := /build/linux_intel64_gcc_android_cc4.9_NDKr10b_version_android-L_release
include $(CLEAR_VARS)
LOCAL_MODULE := PiTBBCalc
LOCAL_SRC_FILES := PiTBBCalc.cpp
LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -fexceptions -Wdeprecated-declarations -I$(TBB_PATH)/include -I$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_LDLIBS := -llog -ltbb -L./ -L$(TBB_PATH)$(TBB_BUILD_PATH)
LOCAL_SHARED_LIBRARIES += libtbb
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libtbb
LOCAL_SRC_FILES := $(TBB_PATH)$(TBB_BUILD_PATH)/libtbb.so
include $(PREBUILT_SHARED_LIBRARY)
В директории jni/ создадим файл Application.mk и добавим в него следующие строки:
APP_ABI := x86_64 APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti APP_STL := gnustl_shared
Здесь назначение Application.mk состоит в описании необходимых нашему приложению нативных модулей (то есть статический/общих библиотек). В строке APP_ABI := x86_64 зададим нашу целевую архитектуру.
Теперь попробуем запустить приложение. Если вы увидите основной экран приложения, значит библиотеки Intel TBB слинковались успешно, и мы можем начинать разработку нашего приложения.
Для добавления параллелизма нам следует включить заголовок Intel TBB: #include «tbb/tbb.h», удалить цикл и добавить следующий код:
double sum = tbb::parallel_reduce(
tbb::blocked_range<unsigned int>(0,n),
double(0), // элемент для суммирования
[&](const tbb::blocked_range<unsigned int>& r, double sum)->double {
for( int i=r.begin(); i!=r.end(); ++i )
{
sum += piIntFunc(x);
x += h;
}
return sum; // тело возвращает обновленное значение для накопителя
},
[]( double x, double y )->double {
return x+y; // соединяет два накопленных значения
}
);
И теперь, если запустить приложение, оно будет работать в многопоточном режиме.
The end
Как видите, разработка параллельных приложений очень проста. Используя в качестве примера вычисление числа Пи, мы смогли успешно продемонстрировать, как концепции однопоточного кода применяются в многопоточном коде.
Хозяйке на заметку:
» Threading Building Blocks – базовые элементы многопоточности
» Создание параллельных Android-приложений для 64-разрядной архитектуры с использованием Intel TBB
» Руководство по Android: написание многопоточных приложений с помощью Intel Threading Building Blocks