Overview

HUAWEI Accelerate Kit provides multi-thread programming APIs, enhancing the program performance and facilitating the development of multi-core and multi-thread apps.
The provided multi-thread programming APIs serve the following purposes:

What You Will Create

In this codelab, you will implement multi-thread programming.

What You Will Learn

Hardware Requirements

Software Requirements

Required Knowledge

To integrate HUAWEI Accelerate Kit, you must complete the following preparations:

  1. Register as a developer.
  2. Create an Android Studio project.
  3. Generate a signing certificate fingerprint.
  4. Configure the signing certificate fingerprint.

For details, please refer to "Configuring App Information in AppGallery Connect" in Development Guide.

1. Create an Android Studio project.

  1. Create a new project.
  2. Select Native C++.
  3. Set the project name and select a project path. Click Next.
  4. Click Finish. The project is created successfully.

2. Open the /app/build.gradle file and add the following code lines:

3. Copy libraries and header files.

a) Download the SDK package from SDK Download and decompress the package.

b) Copy the header files in the SDK to the resource library.

In the /app directory in the Android Studio project, create an include folder. Copy the files in the /include directory of the SDK to the newly created include folder.

c) Copy the .so files in the SDK to the resource library.

Create a libs folder in the /app directory, and create an arm64-v8a folder and an armeabi-v7a folder in the /app/libs directory.

Copy the libdispatch.so, libdispatch_stat.so, and libBlocksRuntime.so files in the /lib64 directory of the SDK to the /libs/arm64-v8a directory of Android Studio.

Copy the libdispatch.so, libdispatch_stat.so, and libBlocksRuntime.so files in the /lib directory of the SDK to the /libs/armeabi-v7a directory of Android Studio.

d) Modify the app/src/main/cpp/CMakeLists.txt file as follows.

This section provides instructions for calculating π with the Accelerate Kit APIs. The Gregory-Leibniz infinite series is used to approximate π as follows:

To begin with, break the calculation down into eight tasks (tasks 0–7) so that a part of the infinite series is calculated each task, and the value of can be obtained by accumulating the result of each task. Then, the value of π is obvious.

1. Set related parameters, including the number of terms (n), total number of tasks (threads), and initialization of the calculation result (pi). Because pi needs to be updated and modified in a subsequent block, the modifier __block is added.

_block double pi = 0; int n = 1000000; int threads = 8;

2. Call dispatch_queue_create to create a serial queue for π calculation. Then, call dispatch_apply to synchronously execute a task for multiple times in a specified queue. Generally, you are advised to set the specified queue to DISPATCH_APPLY_AUTO, which is a global concurrent queue whose priority is closest to that of the current queue. Since tasks are executed concurrently, and the π calculation in this example is reentrant, it is possible to accelerate the calculation in a manner of concurrent execution.

dispatch_queue_t accumulator = dispatch_queue_create("Compute pi", NULL); dispatch_apply(threads, DISPATCH_APPLY_AUTO, ^(size_t idx){ // Step-by-step calculation of π });

3. Implement the calculation algorithm of π. That is, allocate the n terms into threads tasks (threads = 8). The start and end of each task are calculated based on the task ID (idx) of the current task. flag indicates the plus or minus sign of each term. sum indicates the accumulation result of the current task.

int start = idx * (n / threads); int end = (idx + 1) * (n / threads); double sum = 0; for (int k = start; k < end; k++) { double flag = (k & 1) ? -1.0 : 1.0; sum += flag / (2 * k + 1); }

4. Call dispatch_sync to calculate the sum of each task. Then, obtain by accumulating the results of the eight tasks.

dispatch_sync(accumulator, ^{ pi += sum; });

5. Call dispatch_release to free the space of the accumulator. Objects created by APIs of class dispatch_xxx_create() each correspond to a space on the heap. Because there is no garbage collection mechanism, such user-created objects need to be destroyed proactively when the user determines that the objects are no longer needed.

dispatch_release(accumulator);

6. Output the accumulated string for π to the screen for display. π is kept to 20 decimal places.

extern "C" JNIEXPORT jstring JNICALL Java_com_example_example_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::stringstream ss; ss << std::setprecision(20) << calculate_pi(); return env->NewStringUTF(ss.str().c_str()); }

Accelerated computation of π is complete. The following gives the complete code (app/src/main/cpp/native-lib.cpp).

#include <jni.h> #include <string> #include <sstream> #include <iomanip> #include <android/log.h> #include <dispatch/dispatch.h> double calculate_pi(void) { __block double pi = 0; int n = 1000000; int threads = 8; dispatch_queue_t accumulator = dispatch_queue_create("Compute pi", NULL); dispatch_apply(threads, DISPATCH_APPLY_AUTO, ^(size_t idx){ int start = idx * (n / threads); int end = (idx + 1) * (n / threads); double sum = 0; for (int k = start; k < end; k++) { double flag = (k & 1) ? -1.0 : 1.0; sum += flag / (2 * k + 1); } dispatch_sync(accumulator, ^{ pi += sum; }); }); dispatch_release(accumulator); return pi * 4; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_example_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::stringstream ss; ss << std::setprecision(20) << calculate_pi(); return env->NewStringUTF(ss.str().c_str()); }

Well done. You have successfully completed this codelab and learned how to:

For more information, please refer to related documents.

Download the demo source code used in this codelab from the following address:

Download source code

Code copied