鉴于许多 Android* 设备的处理器都具有一个以上的内核,了解如何开发多线程应用在移动行业变得十分重要。 英特尔开发了一种名为英特尔® 线程构建模块(英特尔® TBB)的重要工具,该工具可用于开发并行应用。 英特尔® TBB 是一个跨平台模板库,用于帮助用户创建并行程序。 它可以创建和同步数据流,同时隐藏架构详情,支持您在更高的抽象化水平上工作。 英特尔® TBB 可以在所有架构上运行。 对于 Android,请使用 4.3 及更高版本。
构建英特尔® TBB
- 下载 TBB。 您可以在此处下载英特尔® TBB: https://www.threadingbuildingblocks.org/。 我下载了最新的稳定版(4.3 Update 1)。
- 将 NDK 添加至路径:
对于 Windows*:$ SET PATH=%PATH%;
$ export PATH=$PATH:
- 解压 TBB,转至包含源代码的目录,就在 src 文件夹中。
$ cd /src/
- 运行面向 Android 的 TBB:
$ /ndk-build –C /src/ arch=intel64 compiler=gcc target=android clean tbb tbbmalloc –j 此命令可构建面向 64 位 Android 的 TBB。 若要构建面向 32 位 Android 的 TBB,请更改使用
arch=intel64 on arch=ia32
。 它可以构建面向 Android 64 位架构的应用。 库构建完成。 在 build 目录 (/build/) 中,您可以找到包含 libs 的目录: libgnustl_shared.so, libtbbmalloc_proxy.so, libtbbmalloc.so and libtbb.so. libtbb.so and libgnustl_shared
。我们会在应用开发中用到这些目录。
计算 π
计算 π 时,您可以从维基百科中选择任何一个包含定积分的公式: https://en.wikipedia.org/wiki/List_of_formulae_involving_%CF%80#Integrals。 我选择了这个公式:
针对该程序,我对这个公式进行了修改:
在积分计算中,我采用了矩形法。 积分函数除以 N = 107等于子区间长度 h = 2·10-7。 然后将 N个矩形的面积(底乘以高)加起来计算近似积分,给出以下公式:
创建应用
如果要创建一款新的 Android 应用:
创建 Main Activity
在 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="intel.example.pitbbcalc.MainActivity"><LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"><TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/title" android:textAppearance="?android:attr/textAppearanceLarge" /><Button android:id="@+id/startButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/start" /><LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"><TextView android:id="@+id/pi_equally" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pi_equally" android:textAppearance="?android:attr/textAppearanceLarge" /><TextView android:id="@+id/pi_val" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" /></LinearLayout></LinearLayout></RelativeLayout>
在 res/values/strings.xml
中添加以下代码:
<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">PiTBBCalc</string><string name="action_settings">Settings</string><string name="start">Start</string><string name="title">Calculation of π</string><string name="pi_equally">π = </string></resources>
现在 Main Activity 如下所示:
接下来,我们需要为我们的 Activity 实施 Java* 接口。 在 src/intel.example.pitbbcalc/MainActivity.java
文件中,添加以下代码:
package intel.example.pitbbcalc; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private native double onClickCalc(); private TextView piEqually; private TextView piVal; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startButton = (Button) findViewById(R.id.startButton); piEqually = (TextView) findViewById(R.id.pi_equally); piVal = (TextView) findViewById(R.id.pi_val); piEqually.setVisibility(View.INVISIBLE); piVal.setVisibility(View.INVISIBLE); startButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub double val = onClickCalc(); piVal.setText(String.valueOf(val)); piEqually.setVisibility(View.VISIBLE); piVal.setVisibility(View.VISIBLE); } }); System.loadLibrary("PiTBBCalc"); System.loadLibrary("tbb"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
通过右击 Project Explorer -> Android Tools -> Add Native Support 中的项目,我们可以添加本地支持。
在下一个窗口中输入我们项目库的名称并点击完成。
现在我们要实施原生代码在单线程中执行积分计算。 在 jni/PiTBBCalc.cpp
文件中,添加以下代码:
#include <jni.h> #include <math.h> double piIntFunc (const double x) { return sqrt(1 - pow(x, 2.0)); } double calcPi() { const unsigned int n = pow(10.0, 7); double a(-1), b(1); double h = (b - a) / n; double x (a); for (unsigned int i (0); i < n; ++i) { sum += piIntFunc(x); x += h; } sum *= h; return 2 * sum; } extern "C" JNIEXPORT jdouble JNICALL Java_intel_example_pitbbcalc_MainActivity_onClickCalc(JNIEnv *env, jobject obj) { return calcPi(); }
我们可以尝试在单线程模式下运行我们的应用和 π 计算。
如果使用英特尔® TBB 为我们的项目添加并行实施,我们需要编辑 jni/Android.mk
。 Android.mk 是项目的生成文件(Makefile),我们需要将英特尔® 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 := PiTBBCalc LOCAL_SRC_FILES := PiTBBCalc.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
中,我们可以指定目标架构。
现在我们可以尝试运行我们的应用。 如果您看到了应用的主界面,则说明英特尔® TBB 链接成功,我们可以开始开发应用。
向代码添加并行功能时,我们应加上英特尔® TBB 标题: #include "tbb/tbb.h"
,移除周期并添加以下代码:
double sum = tbb::parallel_reduce( tbb::blocked_range<unsigned int>(0,n), double(0), // identity element for summation [&](const tbb::blocked_range<unsigned int>& r, double sum)->double { for( int i=r.begin(); i!=r.end(); ++i ) { sum += piIntFunc(x); x += h; } return sum; // body returns updated value of accumulator }, []( double x, double y )->double { return x+y; // joins two accumulated values } );
现在,如果您运行该应用,它就可以在多线程模式下运行了。
总结
如您所见,开发并行应用非常简单。 以计算 π 为例,我们成功演示了如何从单线程模式到多线程模式运用这些概念。