计算&IO密集型任务的 优化

问题原因:

最近由于工作实际需求,需要对某个计算单元的计算方法进行重构。原因是由于这个计算单元的计算耗时较长,单个计算耗时大约在1s-2s之间,而新的需求下,要求在20s内对大约1500个计算单元计算完毕。如果不对原有计算单元的计算方法进行优化及效率提升,那么以8核CPU(超线程16线程)来说,在单个计算1s的理想条件,服务器16线程完成任务的理论上限也需要90s+,何况多线程还并不是简单的效率叠加,实际测试情况下,耗时往往在150s以上。因此,对原有计算单元的计算优化是必须的。

问题分析:

通过对原有计算单元的实现过程查看,计算任务存在大量的数据库读取及大量的比对、计算等操作,涉及的数据表的数量级从数百到百万不等,这些数据表有一个相同的特点就是相对固定,并不是实时业务数据。通过对原有计算过程的分析,主要的耗时就在各种条件比对及比对后进行的数据库IO操作。

因此,要提高效率,首先想到的就是如何减少数据库的IO次数,但实际的计算任务是一个很严格的时序型逻辑,即每一步的处理输入是上一步的数据处理结果。因此要在单个计算任务中进行并行计算的改造很难,并且,由于单次数据库IO的时间开销也不大,因此进行异步化改造也不合适,反而会增加代码的复杂度。

所以最终还是把思路集中在如何对计算任务本身进行优化。

解决方式:

经过对计算任务的分析,在这种场景上下文中,决定以哈希定位作为解决方式(这是一种可能的解决方式,但并不一定是最优的)。

通过前文对计算任务的分析,所涉及的数据都是相对固定的,因此首先考虑将所有数据加载到内存(由于数据量并不是非常的大,服务器内存还能承受,可根据实际需求加载到mem或redis中)。如果仅仅是将数据加载到内存,再用linq2object替代原有的数据库IO,提升并不大,因为计算逻辑中最耗时的操作是对数据的范围查询,即数据并没有精确匹配,而是需要找到目标值对应数据的上下限,并进行线性插值运算。

如果能将范围数据查询的工作以更快速更精确的方式来实现,就省下了计算逻辑中最大的时间开销。因此考虑才用呢哈希定位的方式进行。

具体改造过程不再赘述,工作难点主要在于哈希KEY的构造,以及如何通过哈希寻址实现数据库查询中的‘> and <’条件操作。具体来说,通过将范围值扩大量纲变为整数,并以最小步长提前做线性插值,即可形成满足要求的哈希KEY,同时,通过对需要定位的值,对步长进行除法取整,即可得到目标值的下限值,再对下限值加上步长,即可得到上限值,从而通过一次哈希寻址,得到之前需要在数据库进行‘> and <’操作的结果。

解决结果:

通过以上改造,在该计算任务场景中,对1000+计算单元进行计算的时间开销已降低到1-4秒(由于是WCF服务调用,因此需要视网络通信等状况而定),完全可以满足需求。

通过对这次计算任务的重构,可以看出,对计算密集型/IO密集型任务,异步化及并行计算等优化方法很难进行,并且提高会非常有限(计算密集型任务),因此,通过对原子任务本身的优化来达到最终目标也是一个重要的思路。

原文地址:https://www.cnblogs.com/you-you-111/p/4592393.html