Click here to Skip to main content
15,883,901 members
Articles / Mobile Apps / Android
Article

Parallel Android Applications for 64-bit architecture with Intel® TBB

Rate me:
Please Sign up or sign in to vote.
4.71/5 (4 votes)
1 Jun 2015CPOL3 min read 8.1K   3  
In this paper I will show you how easy it is to develop parallel applications for Android L 64 bit with Intel® Threading Building Blocks (Intel® TBB).

This article is for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers

Intel® Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed. Join our communities for Android, Internet of Things, Intel® RealSense™ Technology, and Windows to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathon’s, contests, roadshows, and local events.

Recently, the new Android L 64-bit OS became available. In this paper I will show you how easy it is to develop parallel applications for Android L 64 bit with Intel® Threading Building Blocks (Intel® TBB). The Intel TBB is a cross-platform library of templates for creating parallel programs. It creates and synchronizes streams of data, hiding the details of the architecture and allowing you to work at a higher level of abstraction. Intel TBB works on all architectures. For Android, use version 4.3 and above.

Build Intel® TBB

  1. You can download Intel TBB from here: https://www.threadingbuildingblocks.org/. I downloaded the sources of the last stable release (4.3 Update 1).
  2. Add NDK to PATH:
    For Windows*:
    • $ SET PATH=%PATH%; <path_to_ndk>
    For Linux*:
    • $ export PATH=$PATH: <path_to_ndk>
  3. Unzip Intel TBB and go to the src folder.
    $ cd <tbb_sources>/src/
  4. Build Intel TBB libraries for Android by running: $ <path_to_ndk>/ndk-build –C <tbb_sources>/src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc -j
  5. The library is built. In the build directory (<tbb_sources>/build/) you can find directories with these libs: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. We will use libtbb.so and libgnustl_shared.so in our application.

Configuring the emulator

In Eclipse* click: Window -> Android Virtual Device Manager. In this window click Create and choose the options shown on the next screen:

Click Ok. To check if it’s working, select new device in the list and click Start…. In a new window click the Launch button and the emulator will start:

Create application

In our sample application we will matrix multiply (3x2) times (2x3). Let’s do this!

Create a new Android application:

Add native support by right-clicking on the our project in Project Explorer -> Android Tools -> Add Native Support

On the next window enter the name of the lib of our project and click Finish.

In the project directory, a new jni directory has been created. Android.mk is the Makefile for our project, and we need to add the Intel TBB libs to it:

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)

In the jni directory, create an Application.mk file and add the following lines:

APP_ABI := x86_64
APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti
APP_STL := gnustl_shared

The purpose of Application.mk is to describe which native modules (i.e., static/shared libraries) are needed by your application. In the line APP_ABI := x86_64 we specify our target architecture. It will application for 64-bit architecture.

Now we can try to run our application. If you see the main screen of the application, then Intel TBB linked successfully and we can start developing our matrix multiplication app.

In the res/layout/activity_main.xml paste the following code:

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>

In the res/values/strings.xml add this code:

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>

And now the main activity looks like this:

Next we need to implement the Java* interface for our activity. In the src/intel.example.tppmatrixmult/MainActivity.java file, add the following code:

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);
				}
			}
		}
	}
}

And now we are using Intel TBB constructions for parallel code. The next code snippet is an implementation of matrix multiplication in jni/TBBMatrixMult.cpp. This code is running in one thread:

Java
#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;
}
For adding parallelism to this code, we should include the Intel TBB header: #include "tbb/tbb.h" and change the cycle by row:
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];
		}
	}
});

And now we are using Intel TBB constructions for parallel code. The next code snippet is an implementation of matrix multiplication in jni/TBBMatrixMult.cpp. This code is running in one thread:

Java
#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;
}

For adding parallelism to this code, we should include the Intel TBB header: #include "tbb/tbb.h" and change the cycle by row:

Java
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];
		}
	}
});

You can see the result of our application in the next screenshot:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Intel is inside more and more Android devices, and we have tools and resources to make your app development faster and easier.


Comments and Discussions

 
-- There are no messages in this forum --