算法耗时通用优化技巧 总结

最近在部署AI相关的算法,并要求减少总耗时,从中总结出的一些比较通用的优化技巧。精髓总结一句话就是:在同一时间尽可能充分利用硬件资源。而怎么尽可能充分利用呢,方式就是多线程并行处理

1、单线程串行处理数据

假设算法需要处理两份同类型的数据(适用场景可以扩展到:算法需要处理两份及以上同类型的数据,或者对算法内部进行优化),最简单的就是采用单线程先后处理,如下图1:

 图1 串行处理多份同类型数据

2、多线程并行处理同类型数据

串行方式的缺点是对硬件的使用率非常低,比如处理每份数据都要经过数据加载、计算、保存等操作,那么同一时间数据在加载传输到内存的时候cpu是空闲的,最好的情况是在加载传输第二份数据的时候cpu正在对第一份数据计算。提高各个硬件资源的利用率的方法就是“多线程并行”,如下图2所示创建两个线程,每个线程分别处理一份同类型数据:

 图2 多线程并行处理同类型数据

c++实现代码如下:

#include <thread>

using namespace std;

auto func_proc=[](Ai* mai,float* data){
    mai->Proc(data);
};

Ai mai1,mai2;
float* data1,data2;

thread t1(func_proc,&mai1,data1);
thread t2(func_proc,&mai2,data2);
t1.join();
t2.join();

3、拆分成各子模块之间的并行

但是上面这种并行方式资源占用是比较多的,相当于同时申请了两份Ai资源,如果Ai资源内存或者显存占用很多势必会影响其他算法性能。因此有一种解决方案就是对Ai进行拆分(如下图3所示),比如这里拆分成4个子模块,子模块之间尽可能解耦(可以有相互依赖关系)。这样当线程2处理子模块A时会先等待线程1执行完子模块A,当线程1执行完子模块B时,线程2执行子模块A,达到子模块之间并行。从理论上来说,对算法模块拆分越精细,则对硬件利用率越高。

  图 3.各个子模块之间并行

代码上实现这种并行方式,可以对每个子模块加一个互斥锁,如下图4所示。

  图 4.通过加互斥锁达到各子模块间的并行

c++代码如下:

#include <thread>
#include <mutex>

using namespace std;

auto func_proc=[&](Ai* mai,float* data,mutex* mylocks){
    {
        std::lock_guard<std::mutex> tmpLock(mylocks[0]);
        auto outA=mai->subA(data);
    }
    {
        std::lock_guard<std::mutex> tmpLock(mylocks[1]);
        auto outB=mai->subB(outA);
    }
    {
        std::lock_guard<std::mutex> tmpLock(mylocks[2]);
        auto outC=mai->subC(outB);
    }
    {
        std::lock_guard<std::mutex> tmpLock(mylocks[3]);
        auto outD=mai->subD(outC);
    }
 
};

Ai mai1;
float* data1,data2;
mutex mylocks[4];

thread t1(func_proc,&mai1,data1,mylocks);
thread t2(func_proc,&mai1,data2,mylocks);
t1.join();
t2.join();

4、独立子模块之间的并行 

进一步,如果子模块之间有相互独立的,则可以并行起来,如下图所示:

  图 5.独立子模块之间的并行

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值