Closed bestpower closed 3 years ago
目前TNN可以多线程推理,内部是线程安全的。如果4个模型并行,需要在每个线程中创建独立的Instance,之后使用的方式和单线程一致。
目前TNN可以多线程推理,内部是线程安全的。如果4个模型并行,需要在每个线程中创建独立的Instance,之后使用的方式和单线程一致。
我自测了下,在每个线程中创建独立的Instance需要不少耗时,如果在多线程并行推理中这么做就没有相对串行推理的性能优势了,能否将每个模型的instance在主线程中以static的形式提前创建,并在子线程中调用?另外多线程推理时,针对每个线程中的模型设置SetCpuNumThreads时有什么讲究吗?
可以的,只需要保证不同的线程跑的是不同的instance就行。你用的是什么平台跑的?SetCpuNumThreads这个接口是对openmp的设置,是全局的,多线程里面设置可能会以最后一个执行的为最终结果进行执行。
可以的,只需要保证不同的线程跑的是不同的instance就行。你用的是什么平台跑的?SetCpuNumThreads这个接口是对openmp的设置,是全局的,多线程里面设置可能会以最后一个执行的为最终结果进行执行。
Anrdoid arm平台,编译选项里有加openmp。
在安卓jni层进行多线程并行推理出错,麻烦帮忙看看是什么原因: 出错日志:
01-09 10:48:33.004 11152 11152 F DEBUG : backtrace:
01-09 10:48:33.004 11152 11152 F DEBUG : #00 pc 0008bb18 /data/app/com.demo.adft-1/lib/arm/libTNN.so (_ZN3tnn8Instance11SetInputMatENSt6__ndk110shared_ptrINS_3MatEEENS_15MatConvertParamENS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE+47)
01-09 10:48:33.004 11152 11152 F DEBUG : #01 pc 00031991 /data/app/com.demo.adft-1/lib/arm/libTnnFace.so
01-09 10:48:33.004 11152 11152 F DEBUG : #02 pc 00038f29 /data/app/com.demo.adft-1/lib/arm/libTnnFace.so
其中TNN库出错信息为:
libTNN.so PC=0008bb18
_ZN3tnn8Instance11SetInputMatENSt6__ndk110shared_ptrINS_3MatEEENS_15MatConvertParamENS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
libTnnFace.so 库出错定位位置均是在调用线程函数中的instance时
如:
tnnInOutPutInfos->instance->GetCommandQueue((void**)&command_queue);
TNN_NS::Status status = tnnInOutPutInfos->instance->SetInputMat(target_mat, input_cvt_param, "data");
并行推理关键代码:
auto *tnnInOutPutInfos1 = new TnnInOutPutInfos();
tnnInOutPutInfos1->instance = p_faceLandmark_instance;
tnnInOutPutInfos1->input_mat = input_mat;
tnnInOutPutInfos1->faceInfos = nullptr;
tnnInOutPutInfos1->futureRunningFlag = tnnInOutPutInfos1->promiseRunningFlag.get_future();
auto *tnnInOutPutInfos2 = new TnnInOutPutInfos();
tnnInOutPutInfos1->instance = p_faceOcclusion_instance;
tnnInOutPutInfos2->input_mat = input_mat;
tnnInOutPutInfos2->faceInfos = nullptr;
tnnInOutPutInfos2->futureRunningFlag = tnnInOutPutInfos2->promiseRunningFlag.get_future();
auto *tnnInOutPutInfos3 = new TnnInOutPutInfos();
tnnInOutPutInfos1->instance = p_facePose_instance;
tnnInOutPutInfos3->input_mat = input_mat;
tnnInOutPutInfos3->faceInfos = nullptr;
tnnInOutPutInfos3->futureRunningFlag = tnnInOutPutInfos3->promiseRunningFlag.get_future();
auto *tnnInOutPutInfos4 = new TnnInOutPutInfos();
tnnInOutPutInfos1->instance = p_faceMask_instance;
tnnInOutPutInfos4->input_mat = input_mat;
tnnInOutPutInfos4->faceInfos = nullptr;
tnnInOutPutInfos4->futureRunningFlag = tnnInOutPutInfos4->promiseRunningFlag.get_future();
std::thread t1(faceLandmarkThreadFunc, std::ref(tnnInOutPutInfos1));
std::thread t2(faceOcclusionThreadFunc, std::ref(tnnInOutPutInfos2));
std::thread t3(facePoseThreadFunc, std::ref(tnnInOutPutInfos3));
std::thread t4(faceMaskThreadFunc, std::ref(tnnInOutPutInfos4));
t1.detach();
t2.detach();
t3.detach();
t4.detach();
//获取线程执行结束标志位,如获取不到,则保持等待,直至获取到为止,主线程阻塞
int flag1 = tnnInOutPutInfos1->futureRunningFlag.get();
int flag2 = tnnInOutPutInfos2->futureRunningFlag.get();
int flag3 = tnnInOutPutInfos3->futureRunningFlag.get();
int flag4 = tnnInOutPutInfos4->futureRunningFlag.get();
单线程代码示例:
static void faceLandmarkThreadFunc(TnnInOutPutInfos* &tnnInOutPutInfos){
std::vector<std::string> outputNames;//输入节点名称数组
outputNames.emplace_back("landmark");
std::vector<float *> outputs;//输出数据数组
auto target_mat = std::make_shared<TNN_NS::Mat>(p_device_type, TNN_NS::N8UC4, p_landmark_target_dims);
TNN_NS::ResizeParam param = TNN_NS::ResizeParam();
void* command_queue;
tnnInOutPutInfos->instance->GetCommandQueue((void**)&command_queue);
TNN_NS::MatUtils::Resize(*(tnnInOutPutInfos->input_mat), *target_mat, param, command_queue);
// convert input mat
TNN_NS::MatConvertParam input_cvt_param = TNN_NS::MatConvertParam();
input_cvt_param.scale = p_scale;
input_cvt_param.bias = p_bias;
TNN_NS::Status status = tnnInOutPutInfos->instance->SetInputMat(target_mat, input_cvt_param, "data");
// inference
status = tnnInOutPutInfos->instance->ForwardAsync(nullptr);
// status = faceLandmark_instance->Forward();
// get output data
if (status == TNN_NS::TNN_OK) {
const TNN_NS::MatConvertParam output_cvt_param = TNN_NS::MatConvertParam();
for(int i=0;i<outputNames.size();i++) {
std::shared_ptr<TNN_NS::Mat> output_mat = nullptr;
status = tnnInOutPutInfos->instance->GetOutputMat(output_mat, output_cvt_param, outputNames[i]);
outputs.push_back(static_cast<float *>(output_mat->GetData()));
}
tnnInOutPutInfos->faceInfos = outputs[0];
}
tnnInOutPutInfos->promiseRunningFlag.set_value(1);//设置结束标志位
}
线程传参结构体:
struct TnnInOutPutInfos{
std::shared_ptr<TNN_NS::Instance> instance;//网络instance
std::shared_ptr<TNN_NS::Mat> input_mat;//输入mat
float* faceInfos;//输出信息数组
//异步传参运行标志位
std::promise<int> promiseRunningFlag;
std::future<int> futureRunningFlag;
};
另外,此方式只创建一个线程的模型推理时不会报错,两个及以上就会报错。
多线程之间的资源要保证独立,看你的代码,是否多个线程使用的同一个input_mat,这个input_mat的生命周期是什么样的?建议输入在线程内进行构造。
多线程之间的资源要保证独立,看你的代码,是否多个线程使用的同一个input_mat,这个input_mat的生命周期是什么样的?建议输入在线程内进行构造。
问题解决了,主要是代码问题,是前面有个模型路径的模型名称拼写错了,模型加载的时候没添加异常捕获所以一直没发现,另外不同线程传参名称有搞混的情况,现在可以了
请问TNN在推理时有类似ncnn的绑定大小核的功能吗?
有绑大小核的接口,请参考:
https://github.com/Tencent/TNN/blob/master/include/tnn/utils/cpu_utils.h#L34
其中接口:
Status SetCpuPowersave(int powersave);
Status SetCpuAffinity(const std::vector
有绑大小核的接口,请参考: https://github.com/Tencent/TNN/blob/master/include/tnn/utils/cpu_utils.h#L34 其中接口: Status SetCpuPowersave(int powersave); Status SetCpuAffinity(const std::vector& cpu_list); 都可以实现这个功能。
好的,感谢告知!
目前部署框架均支持同一模型的多线程推理,请问TNN框架在安卓端是否支持不同模型的并行推理? 如果支持的话该如何实现,或者给一个示例代码? 比如设备是4核cpu,我想同时跑四个模型,且四个模型的推理流程均互不干扰,该如何实现?