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:
In this codelab, you will implement multi-thread programming.
To integrate HUAWEI Accelerate Kit, you must complete the following preparations:
For details, please refer to "Configuring App Information in AppGallery Connect" in Development Guide.
arguments "-DANDROID_STL=c++_shared"
: multithread-lib depends on libc++. Therefore, c++_shared needs to be declared in the CMake build parameters. Otherwise, some functions will fail to work.abiFilters "arm64-v8a", "armeabi-v7a"
: multithread-lib supports only arm64-v8a and armeabi-v7a.jniLibs.srcDirs = ['libs']
: specifies the directory of the .so 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.
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
target_include_directories(
native-lib
PRIVATE
${CMAKE_SOURCE_DIR}/../../../include)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
add_library(
dispatch
SHARED
IMPORTED)
set_target_properties(
dispatch
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libdispatch.so)
add_library(
BlocksRuntime
SHARED
IMPORTED)
set_target_properties(
BlocksRuntime
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libBlocksRuntime.so)
target_compile_options(native-lib PRIVATE -fblocks)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
dispatch
BlocksRuntime
# Links the target library to the log library
# included in the NDK.
${log-lib} )
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: