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>
- $ 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:
<?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:
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:
Notices
INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.
UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.
Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.
The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.
Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.
Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm
Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark* and MobileMark*, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.
Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.
Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.