Многие Android-устройства используют процессоры с несколькими вычислительными ядрами, поэтому в отрасли разработки мобильных приложений всё более важным становится умение создавать многопоточные программы. Компания Intel предлагает ценный инструментарий для разработки «параллельных» приложений – он называется Intel Threading Building Blocks (Intel TBB). По существу, Intel TBB представляет собой кросс-платформенную библиотеку шаблонов для создания параллельных программ. Она позволяет создавать и синхронизировать потоки данных, оставляя за кадром детали архитектуры и позволяя вам работать на высоком уровне абстрагирования. Intel TBB поддерживает все архитектуры. Что касается ОС Android, то следует использовать версию 4.3 и выше.

image

Создание Intel TBB


  1. Скачайте Intel TBB, найти можно здесь: www.threadingbuildingblocks.org.

  2. Добавьте NDK в PATH. Для Windows:

    $ SET PATH=%PATH%;

    Для Linux:

    $ export PATH=$PATH:

  3. Распакуйте TBB и перейдите в директорию с исходным кодом и в папку src.

    $ cd /src/

  4. Запустите 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.

  5. Библиотека создана. В соответствующей директории (/build/) вы найдете поддиректории с библиотеками: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so и libgnustl_shared. Теперь их можно использовать в нашем приложении.

Вычисление числа Пи


Для вычисления Пи можно выбрать в Wikipedia любую формулу с определенным интегралом. Я выбрал следующую формулу:

image

Для данной программы я модифицировал эту формулу:

image

Для вычисления интеграла я использовал метод прямоугольников. Интегрируемая функция разбивается на N = 107 равных подинтервалов длиной h = 2·10-7. Затем вычисляется аппроксимация интеграла – сложением площадей (основание х высоту) N прямоугольников по формуле:

image

Создание приложения


Создадим новое Android-приложение:

image

Создадим «основную активность» (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 выглядит примерно так:

image

Далее нам нужно реализовать для своей активности интерфейс 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.

image

Итак, мы реализовали нативный код для вычисления интеграла в одном потоке. В файл 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();
}

Попробуем запустить наше приложение и вычислить число Пи в однопоточном режиме:

image

Чтобы с помощью 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

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


  1. rafuck
    11.06.2015 18:14
    +7

    1) Чуть лучше было бы вычислять интеграл 1/(1+x^2) ввиду отсутствия необходимости извлекать квадратный корень.
    2) Также никакого труда бы не составило использовать составную квадратурную формулу центральных прямоугольников вместо левых. Это было бы эффективнее, чем распараллеливание.
    3) Вместо интеграла от -1 до 1, очевидно следует считать удвоенный интеграл 0 до 1


  1. ilnarb
    16.06.2015 17:44

    С такой формулой далеко не полетишь. Надо брать хотя бы алгоритм Чудновского. Несколько лет назад Intel проводил конкурс.