生物信息学经常需要处理大量的数据,如果只是单线程跑程序的话往往需要几个小时甚至几个月才能跑完一个任务,因此并行执行就变得很重要了。并行可以同时使用电脑的多个线程或者处理器,可以大大降低某个任务的运行时间,所以掌握并行执行程序的方法对于做好生物信息学研究是非常重要的。
许多程序内部会进行并行,这些程序通常会有一个参数表示并行所用的线程数目(例如bcftools的threads参数,minimap2的-t参数等),对于这些程序来说直接通过这些参数指定线程数目即可,不需要手动并行,下面要重点说的是那些只能单线程执行的程序。由于这些程序只能单线程运行,当它们处理很大的数据时就会需要运行很久,具体时间取决于任务量。对于这些程序来说,其实可以采用多核并行,基本要求是每个任务都是独立的最小单元,任务之间不会相互影响。
举例来说,我有一个基因表达量的文件,里面包括了5万个基因的表达量和这些基因的坐标,并且已经基于坐标使用tabix建立好了索引,因此可以使用tabix从该文件中快速提取某个基因的表达量。
假如我需要做一个分析,该分析需要的数据是每个基因的表达量,那么我需要依次取出这5万个基因的表达量,每个基因表达量单独存放到一个文件中,因此需要存放到5万个文件中。对于某个基因来说,我可以获取该基因的坐标,从而使用tabix从大的表达量文件中提取某个基因的表达量。
问题在于,我有5万个基因,这一步骤需要循环执行5万次,这是非常耗时的一项任务,因此可以对该任务进行并行处理。由于每个基因的输出都是一个单独的文件,因此对每个基因的处理不会影响其他基因的结果,满足前面提到的并行条件。
这里推荐一个非常好用的并行软件,叫做paralleltask,是使用python语言编写的一款小而美的软件。这里我不对该软件的具体用法做介绍,参考官网即可:https://github.com/moold/ParallelTask
下面我将介绍一下使用该软件来解决上述任务的思路。
- 首先我们需要将对这5万个基因提取表达量的命令写到一个shell脚本中(该脚本将有5万行tabix命令);
- 其次paralleltask会读取该脚本,并将每lines行作为一个子任务(lines由用户指定)。对于上述任务来说,n可以设置为1,这样共有5万个子任务;
- 接下来paralleltask会针对每个子任务新建一个文件夹,并将该子任务的命令写到该文件夹下面的shell脚本中。经过这一步以后,当前目录下将会有5万个文件夹,其中每个文件夹都有一个shell脚本,该脚本中是每个子任务的命令;
- 接下来paralleltask会依次执行这些子任务,且同时最多执行max个子任务(max也由用户指定),例如我可以同时执行100个任务,那么就设置max为100。当之前的任务结束后,paralleltask会在该任务所在目录下新建一个done文件,表示该任务已完成;接下来paralleltask会继续运行新的任务,确保同一时刻最多只有100个任务在运行;
- 最后当paralleltask执行完所有任务以后,便会退出程序。
在上述过程中,有一个参数可以进行优化,即参数lines的确定。前面为了方便,我将lines设置成了1,这样最终就有5万个子任务。
这里需要考虑一个时间开销的平衡,即paralleltask为了执行任务所花费的时间和实际任务执行时间的平衡。拿卖草莓做一个简单的类比的话,假如我需要从东北运送一车草莓到其他城市,那么草莓的价格将是草莓本身的价格加上运费的一个综合,而草莓本身的价格是固定的,那么距离越远,运费越贵,最终草莓的价格也就会越贵。在paralleltask执行任务时,paralleltask为了执行任务所花费的时间可以类比为草莓的运费,而实际任务的执行时间可以类比为草莓本身的价格。
那么paralleltask为了执行任务都做了哪些事情呢?(即运费都花在哪里了?)paralleltask会对每个任务都新建一个目录,而新建5万个目录将会花费很久(可能需要好几分钟),此外对每个任务都需要执行该任务、检查是否结束等步骤,这些也是比较耗时的步骤。
如果每个任务实际执行时间很短(即物品本身价格比较便宜),那么最终就会出现运费比物品本身价格更贵的现象,这显然不合理。
为了解决这个问题,核心思路就是降低运费,即缩短paralleltask为了执行任务所花费的时间,因此需要缩小子任务数目,即增加参数lines的大小。
这个参数n的选取并没有一个绝对的指标,我建议以保持每个任务的时间在几分钟左右作为标准来选取参数n。例如对于上面的提取基因表达量的任务,可以设置lines为100,即100个基因为一个子任务,这样总共需要执行500个子任务,将会大大paralleltask所需的额外时间(即运费)。
总结
在使用paralleltask进行并行执行任务时,需要控制总的任务数目不要太大(增加参数lines的大小),否则会有很大的开销都用来新建目录和分配任务,从而造成不必要的浪费。