全新的 Android L 64 位操作系统已于近日推出。 在本文中,我将向您展示如何利用英特尔® 线程构建模块(英特尔® TBB)轻松地开发面向 Android L 64 位架构的并行应用。 英特尔® TBB 是一个跨平台模板库,可用于创建并行程序。 它可以创建和同步数据流,同时隐藏架构详情,支持您在更高的抽象化水平上工作。 英特尔® TBB 可以在所有架构上运行。 对于 Android,请使用 4.3 及更高版本。
构建英特尔® TBB
- 您可以在此处下载英特尔® TBB: https://www.threadingbuildingblocks.org/. 我下载了最新的稳定版(4.3 Update 1)。
- 将 NDK 添加至路径:
对于 Windows*:- $ SET PATH=%PATH%; <path_to_ndk>
- $ export PATH=$PATH: <path_to_ndk>
- 解压 TBB,转至 src 文件夹。
$ cd <tbb_sources>/src/ - 通过运行以下代码构建面向 Android 的英特尔 TBB 库: $ <path_to_ndk>/ndk-build –C <tbb_sources>/src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc -j
- 库构建完成。 在 build 目录 (<tbb_sources>/build/) 中,您可以找到包含这些 libs 的目录: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so and libgnustl_shared。 我们会在应用中用到 libtbb.so 和 libgnustl_shared.so。
配置仿真器
在 Eclipse* 中点击: Window -> Android Virtual Device Manager. 在该窗口中点击 Create 并在下一个界面上选择显示的选项:
点击 Ok。 如要检查它是否工作,选择列表中的新设备并点击 Start... 在新窗口中,点击 Launch 按钮启动仿真器:
创建应用
在我们的样本应用中,我们会采用矩阵相乘 (3x2) 乘以 (2x3)。 让我们开始吧!
创建一个新的 Android 应用:
通过右击我们在 Project Explorer -> Android Tools -> Add Native Support 中的项目,我们可以添加本地支持。
在下一个窗口中输入项目库的名称并点击 Finish。
在项目目录中,一个新的 jni 目录创建完成了。 Android.mk 是项目的生成文件,我们需要将英特尔® 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 := 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 位架构的应用。
现在我们可以尝试运行我们的应用。 如果您看到了应用的主界面,那就表明英特尔® 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>
现在我们的主活动如下所示:
接下来我们需要为我们的活动实施 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); } } } } } 现在我们正在使用面向并行代码的英特尔 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; } 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]; } } });
现在我们正在使用面向并行代码的英特尔 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; }
向代码添加并行功能时,我们应加上英特尔® 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]; } } });
您可以在下一个屏幕截图上看到我们应用的结果: