for循環並行
概念性的東西可以參考c++ 對for循環的並行優化例子,此文中使用多線程對for循環進行了優化,並提出可能遇到的一些問題。
實際上for循環還有一種可用的優化方法是使用OpenMP來進行多線程的加速。OpenMp提供了對於並行描述的高層抽象,降低了並行編程的難度和複雜度,這樣程序員可以把更多的精力投入到並行算法本身,而非其具體實現細節。
實際在一些開源庫當中,大多數都使用OpenMP來進行多線程的優化,比如MNN和NCNN。今天介紹的是MNN中對for循環的並行處理。
這部分內容包含在MNN/source/core/Concurrency.h
中。
Concurrency.h
MNN對並行處理進行了很好的封裝,一個宏MNN_CONCURRENCY_BEGIN
就可以對for循環並行處理。
//
// Concurrency.h
// MNN
//
// Created by MNN on 2018/07/26.
// Copyright © 2018, Alibaba Group Holding Limited
//
#ifndef concurrency_h
#define concurrency_h
#ifdef MNN_FORBIT_MULTI_THREADS //如果禁用多線程
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) for (int __iter__ = 0; __iter__ < __num__; __iter__++) {
#define MNN_CONCURRENCY_END() }
#elif defined(MNN_USE_THREAD_POOL) //使用線程池
#include "ThreadPool.hpp"
#define MNN_STRINGIFY(a) #a
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) \
{std::pair<std::function<void(int)>, int> task;task.second = __num__;\
task.first = [&](int __iter__) {\
#define MNN_CONCURRENCY_END() };\
auto cpuBn = (CPUBackend*)backend();\
MNN::ThreadPool::enqueue(std::move(task), cpuBn->taskIndex());}
#else //不使用線程池,使用操作系統和編程語言提供的多線程特性
// iOS / OSX
#if defined(__APPLE__) //在iOS上使用dispatch
#include <dispatch/dispatch.h>
#include <stddef.h>
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) \
dispatch_apply(__num__, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t __iter__) {
#define MNN_CONCURRENCY_END() \
});
// Windows 在windows上使用openmp
#elif defined(_MSC_VER)
#include <omp.h>
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) \
__pragma(omp parallel for) for (int __iter__ = 0; __iter__ < __num__; __iter__++) {
#define MNN_CONCURRENCY_END() }
#define MNN_CONCURRENCY_BEGIN_CONDITION(__iter__, __num__, __condition__) \
int __iter__ = 0; \
__pragma(omp parallel for if(__condition__)) \
for (; __iter__ < __num__; __iter__++) {
// Android 在其他平臺上使用openmp
#else
#include <omp.h>
#define MNN_STRINGIFY(a) #a
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) \
_Pragma("omp parallel for") for (int __iter__ = 0; __iter__ < __num__; __iter__++) {
#define MNN_CONCURRENCY_END() }
#endif
#endif
#endif /* concurrency_h */
OpenMP部分
重點是MNN_CONCURRENCY_BEGIN
和MNN_CONCURRENCY_END
兩個宏定義
#define MNN_CONCURRENCY_BEGIN(__iter__, __num__) \
_Pragma("omp parallel for") for (int __iter__ = 0; __iter__ < __num__; __iter__++) {
#define MNN_CONCURRENCY_END() }
使用實例
將第一個展開就是:
#pragma omp parallel for
for(int tid = 0; tid < size; tid++){
auto &unit = mUnits[tId];
if (unit.mValid) {
unit.mStracssenComputor->onExecute();
unit.mPostExecutor();
}
}