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
- You can download Intel TBB from here: https://www.threadingbuildingblocks.org/. I downloaded the sources of the last stable release (4.3 Update 1).
- Add NDK to PATH:
For Windows*:
- $ SET PATH=%PATH%; <path_to_ndk>
For Linux*:
- $ export PATH=$PATH: <path_to_ndk>
- Unzip Intel TBB and go to the src folder.
$ cd <tbb_sources>/src/ - 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
- 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:
<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:
="1.0"="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:
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:
#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:
#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];
}
}
});
You can see the result of our application in the next screenshot: