Quantcast
Channel: Статьи Intel Developer Zone
Viewing all articles
Browse latest Browse all 357

Разработка приложений Android* с поддержкой параллельных вычислений для 64-разрядных архитектур с помощью Intel® TBB

$
0
0

9 декабря 2014 г.

Недавно была выпущена новая 64-разрядная ОС Android L. В этой статье мы продемонстрируем, насколько Intel® Threading Building Blocks (Intel® TBB) упрощает разработку приложений с поддержкой параллельных вычислений для 64-разрядной ОС Android L. Intel TBB — это межплатформенная библиотека шаблонов для создания программ, поддерживающих параллельные вычисления. Она создает и синхронизирует потоки данных, что позволяет скрыть особенности архитектуры и работать на более высоком уровне абстракции. Библиотека Intel TBB совместима с любыми архитектурами. При разработке приложений Android рекомендуется использовать версию 4.3 или более позднюю.

Сборка библиотеки Intel® TBB

Для ОС Linux*:

  1. Загрузить Intel TBB можно по адресу https://www.threadingbuildingblocks.org/. В этой статье представлен исходный код последнего стабильного выпуска (версия 4.3, обновление 1).
  2. Добавьте компонент NDK в переменную среды PATH.
    Для ОС Windows:
    • $ SET PATH=%PATH%; <path_to_ndk>
  3. $ export PATH=$PATH: <path_to_ndk>
  4. Распакуйте Intel TBB и перейдите к папке src.
    $ cd <tbb_sources>/src/
  5. Соберите библиотеки Intel TBB для Android, выполнив следующую команду: $ <path_to_ndk>/ndk-build –C <tbb_sources>/src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc -j
  6. Процесс сборки библиотеки завершен. Папка build (<tbb_sources>/build/) содержит подпапки с библиотеками libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so и libtbb.so. Для приложения, описываемого в этой статье, потребуются библиотеки libtbb.so и libgnustl_shared.so.

Настройка эмулятора

В среде Eclipse* нажмите Window -> Android Virtual Device Manager. В открывшемся окне нажмите Create и выберите параметры, как показано на снимке экрана ниже.

Нажмите кнопку ОК. Чтобы проверить работоспособность нового виртуального устройства, выберите его в списке и нажмите Start… В новом окне нажмите кнопку Launch, чтобы запустить эмулятор.

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

В качестве примера будет создано приложение для перемножения матриц (3x2) и (2x3). Рассмотрим все этапы процесса.

Создайте новое приложение Android.

Добавьте поддержку нативного кода, щелкнув правой кнопкой мыши в обозревателе проектов и выбрав последовательно Android Tools -> Add Native Support

В следующем окне введите название библиотеки, используемой для проекта и нажмите кнопку Finish.

В папке проекта была создана новая папка jni. Необходимо добавить библиотеки Intel TBB в MK-файл для этого проекта — Android.mk.

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    := TBBMatrixMult
LOCAL_SRC_FILES := TBBMatrixMult.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 задает целевую архитектуру. Это приложение предназначено для 64-разрядных архитектур.

Попробуем запустить приложение. Появление основного экрана приложения означает, что библиотека Intel TBB подключена успешно и теперь можно приступать к разработке программы для умножения матриц.

Вставьте в файл 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=".MainActivity"><ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"><LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"><TextView
                android:id="@+id/titleA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@string/a"
                android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a00"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a01"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned"><requestFocus /></EditText></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a10"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a11"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/a20"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/a21"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><TextView
		        android:id="@+id/titleB"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="15dp"
		        android:text="@string/b"
		        android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/b00"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b01"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b02"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><EditText
		            android:id="@+id/b10"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b11"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /><EditText
		            android:id="@+id/b12"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:layout_weight="1"
		            android:ems="3"
		            android:inputType="numberDecimal|numberSigned" /></LinearLayout><Button
		        android:id="@+id/button"
		        style="?android:attr/buttonStyleSmall"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="17dp"
		        android:text="@string/button" /><TextView
		        android:id="@+id/titleC"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="@string/c"
		        android:textAppearance="?android:attr/textAppearanceSmall" /><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c00"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c01"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c02"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c10"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c11"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c12"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout><LinearLayout
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"><TextView
		            android:id="@+id/c20"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:layout_marginBottom="40dp"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c21"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /><TextView
		            android:id="@+id/c22"
		            android:layout_width="70dp"
		            android:layout_height="wrap_content"
		            android:textAppearance="?android:attr/textAppearanceSmall" /></LinearLayout></LinearLayout></ScrollView></RelativeLayout>

В файл res/values/strings.xml необходимо добавить следующие строки кода.

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">TBBMatrixMult</string><string name="action_settings">Settings</string><string name="a">A:</string><string name="b">B:</string><string name="c">C:</string><string name="button">A x B = C</string></resources>

После этого Main Activity будет выглядеть следующим образом.

 

Далее необходимо реализовать в этом действии интерфейс Java*. Добавьте в файл src/intel.example.tppmatrixmult/MainActivity.javaследующий фрагмент кода:

package intel.example.tbbmatrixmult;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private native double [][] onClickCalc(double [] a, double [] b, int aRow, int aCol);
	private EditText matrixA [][] = new EditText[3][2];
	private EditText matrixB [][] = new EditText[2][3];
	private TextView matrixC [][] = new TextView[3][3];
	private TextView titleC;
	private Button mult;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initA();
		initB();
		initC();
		mult = (Button) findViewById(R.id.button);
		mult.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
				double [][] a = getA();
				double [][] b = getB();
				if (a == null || b == null) {
					Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
					return;
				}
				double [][] c = onClickCalc(matrixToArray(a), matrixToArray(b), 3, 2);
				setC(c);
				setVisibleC(true);
			}
		});
		setVisibleC(false);

		System.loadLibrary("tbb");
        System.loadLibrary("TBBMatrixMult");
	}

	private double [] matrixToArray(double [][] matrix) {
		double [] array = new double[matrix.length * matrix[0].length];
		for (int row = 0; row < matrix.length; ++row) {
			for (int col = 0; col < matrix[row].length; ++col)
			{
				array[row * matrix[row].length + col] = matrix[row][col];
			}
		}
		return array;
	}

	private void initA() {
		matrixA[0][0] = (EditText) findViewById(R.id.a00);
		matrixA[0][1] = (EditText) findViewById(R.id.a01);
		matrixA[1][0] = (EditText) findViewById(R.id.a10);
		matrixA[1][1] = (EditText) findViewById(R.id.a11);
		matrixA[2][0] = (EditText) findViewById(R.id.a20);
		matrixA[2][1] = (EditText) findViewById(R.id.a21);
	}
	private void initB() {
		matrixB[0][0] = (EditText) findViewById(R.id.b00);
		matrixB[0][1] = (EditText) findViewById(R.id.b01);
		matrixB[0][2] = (EditText) findViewById(R.id.b02);
		matrixB[1][0] = (EditText) findViewById(R.id.b10);
		matrixB[1][1] = (EditText) findViewById(R.id.b11);
		matrixB[1][2] = (EditText) findViewById(R.id.b12);
	}
	private void initC() {
		titleC = (TextView) findViewById(R.id.titleC);
		matrixC[0][0] = (TextView) findViewById(R.id.c00);
		matrixC[0][1] = (TextView) findViewById(R.id.c01);
		matrixC[0][2] = (TextView) findViewById(R.id.c02);
		matrixC[1][0] = (TextView) findViewById(R.id.c10);
		matrixC[1][1] = (TextView) findViewById(R.id.c11);
		matrixC[1][2] = (TextView) findViewById(R.id.c12);
		matrixC[2][0] = (TextView) findViewById(R.id.c20);
		matrixC[2][1] = (TextView) findViewById(R.id.c21);
		matrixC[2][2] = (TextView) findViewById(R.id.c22);
	}

	private double[][] getA() {
		double [][] ret = new double [matrixA.length][];
		for (int i = 0; i < matrixA.length; ++i) {
			ret[i] = new double [matrixA[i].length];
			for (int j = 0; j < matrixA[i].length; ++j) {
				if (matrixA[i][j].getText().toString().length() == 0) {
					Toast.makeText(getBaseContext(), "Error! One field is empty!", Toast.LENGTH_SHORT).show();
					return null;
				}
				ret[i][j] = Double.parseDouble(matrixA[i][j].getText().toString());
			}
		}
		return ret;
	}

	private double[][] getB() {
		double [][] ret = new double [matrixB.length][];
		for (int i = 0; i < matrixB.length; ++i) {
			ret[i] = new double [matrixB[i].length];
			for (int j = 0; j < matrixB[i].length; ++j) {
				if (matrixB[i][j].getText().toString().length() == 0) {
					Toast.makeText(getBaseContext(), "Error! One field is empty!", Toast.LENGTH_SHORT).show();
					return null;
				}
				ret[i][j] = Double.parseDouble(matrixB[i][j].getText().toString());
			}
		}
		return ret;
	}

	private void setC(double [][] cVal) {
		if (matrixC.length != cVal.length) {
			Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
			return;
		}
		for (int i = 0; i < matrixC.length; ++i) {
			if (matrixC[i].length != cVal[i].length) {
				Toast.makeText(getBaseContext(), "Unknown error!", Toast.LENGTH_SHORT).show();
				return;
			}
			for (int j = 0; j < matrixC[i].length; ++j) {
				matrixC[i][j].setText(String.valueOf(cVal[i][j]));
			}
		}
	}

	private void setVisibleC(boolean cond) {
		if (cond == true)
			titleC.setVisibility(View.VISIBLE);
		else
			titleC.setVisibility(View.GONE);
		for (int i = 0; i < matrixC.length; ++i) {
			for (int j = 0; j < matrixC[i].length; ++j) {
				if (cond == true) {
					matrixC[i][j].setVisibility(View.VISIBLE);
				}
				else {
					matrixC[i][j].setVisibility(View.GONE);
				}
			}
		}
	}
}

А теперь мы используем конструкции Intel TBB для параллельного кода. Следующий фрагмент кода представляет собой реализацию перемножения матриц в jni/TBBMatrixMult.cpp. Код выполняется в один поток.

double ** arrayToMatrix(double * array, int row, int col)
{
	double ** matrix = new double * [row];
	for (int i = 0; i < row; ++i)
	{
		matrix[i] = new double [col];
		for (int j = 0; j < col; ++j)
		{
			matrix[i][j] = array[i * col + j];
		}
	}
	return matrix;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_intel_example_tbbmatrixmult_MainActivity_onClickCalc(
		JNIEnv *env, jobject obj, jdoubleArray aMatrix, jdoubleArray bMatrix, jint aRow, jint aCol)
{
	double * aArray = (*env).GetDoubleArrayElements(aMatrix, 0);
	double ** a = arrayToMatrix(aArray, aRow, aCol);
	double * bArray = (*env).GetDoubleArrayElements(bMatrix, 0);
	double ** b = arrayToMatrix(bArray, aCol, aRow);
	double ** c = new double * [aRow];
	for (int i = 0 ; i < 3; ++i) {
		c[i] = new double [aRow];
	}
	for (int row = 0; row < aRow, ++row) {
		for (int col = 0; col < aRow; ++col) {
			c[row][col] = 0;
			for(int k = 0; k < aCol; ++k)
			{
				c[row][col] += a[row][k] * b[k][col];
			}
		}
	};

	jclass doubleArrayClass = (*env).FindClass("[D");

	jobjectArray cMatrix = (*env).NewObjectArray((jsize) aRow, doubleArrayClass, 0);

	for (int i = 0; i < aRow; i++)
	{
		jdoubleArray doubleArray = (*env).NewDoubleArray(aRow);
		(*env).SetDoubleArrayRegion(doubleArray, (jsize) 0, (jsize) aRow, c[i]);
		(*env).SetObjectArrayElement(cMatrix, (jsize) i, doubleArray);
		(*env).DeleteLocalRef(doubleArray);
	}

	return cMatrix;
}
Для добавления параллелизма в этот код мы должны включить заголовок Intel TBB: #include "tbb/tbb.h"и изменить цикл
tbb::parallel_for (0, aRow, 1, [=](int row) {
	for (int col = 0; col < aRow; ++col) {
		c[row][col] = 0;
		for(int k = 0; k < aCol; ++k)
		{
			c[row][col] += a[row][k] * b[k][col];
		}
	}
});

  А теперь мы используем конструкции Intel TBB для параллельного кода. Следующий фрагмент кода представляет собой реализацию перемножения матриц в jni/TBBMatrixMult.cpp. Код выполняется в один поток.

#include <jni.h>

double ** arrayToMatrix(double * array, int row, int col)
{
	double ** matrix = new double * [row];
	for (int i = 0; i < row; ++i)
	{
		matrix[i] = new double [col];
		for (int j = 0; j < col; ++j)
		{
			matrix[i][j] = array[i * col + j];
		}
	}
	return matrix;
}

extern "C" JNIEXPORT jobjectArray JNICALL Java_intel_example_tbbmatrixmult_MainActivity_onClickCalc(
		JNIEnv *env, jobject obj, jdoubleArray aMatrix, jdoubleArray bMatrix, jint aRow, jint aCol)
{
	double * aArray = (*env).GetDoubleArrayElements(aMatrix, 0);
	double ** a = arrayToMatrix(aArray, aRow, aCol);
	double * bArray = (*env).GetDoubleArrayElements(bMatrix, 0);
	double ** b = arrayToMatrix(bArray, aCol, aRow);
	double ** c = new double * [aRow];
	for (int i = 0 ; i < 3; ++i) {
		c[i] = new double [aRow];
	}
	for (int row = 0; row < aRow, ++row) {
		for (int col = 0; col < aRow; ++col) {
			c[row][col] = 0;
			for(int k = 0; k < aCol; ++k)
			{
				c[row][col] += a[row][k] * b[k][col];
			}
		}
	};

	jclass doubleArrayClass = (*env).FindClass("[D");

	jobjectArray cMatrix = (*env).NewObjectArray((jsize) aRow, doubleArrayClass, 0);

	for (int i = 0; i < aRow; i++)
	{
		jdoubleArray doubleArray = (*env).NewDoubleArray(aRow);
		(*env).SetDoubleArrayRegion(doubleArray, (jsize) 0, (jsize) aRow, c[i]);
		(*env).SetObjectArrayElement(cMatrix, (jsize) i, doubleArray);
		(*env).DeleteLocalRef(doubleArray);
	}

	return cMatrix;
}

Для добавления параллелизма в этот код мы должны включить заголовок Intel TBB: #include "tbb/tbb.h"и изменить цикл

tbb::parallel_for (0, aRow, 1, [=](int row) {
	for (int col = 0; col < aRow; ++col) {
		c[row][col] = 0;
		for(int k = 0; k < aCol; ++k)
		{
			c[row][col] += a[row][k] * b[k][col];
		}
	}
});

Результат работы приложения представлен на снимке экрана ниже.


Viewing all articles
Browse latest Browse all 357

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>