大规模时序预测项目中分布式进程的使用

手头有一个时间序列预测框架,串行运行,效率比较慢 ,考虑用多进程、分布式等方法来优化。本文比较随意地记录下工作和一些笔记。

一、性能瓶颈分析

将整个框架按模块划分,揭露各部分耗时,确定:

  1. 耗时占比最高的是特征计算的51.1% ;
  2. 其次是获取原始数据的27.5%;
  3. 然后是预测部分,耗时占比为15.9%。

分析这三个部分的特点可知:
特征计算获取数据两个部分的特点在于它们从数据库读写数据,需要耗费较长时间;
特征计算预测部分的特点在于其内部调用算法进行预测。

所以初步判断“从数据库读取数据”和“预测”是对时序框架效率影响最大的步骤。为了验证以上判断,取若干配置项进行实验,分别记录以上三个部分中的“读取数据”和“预测”的耗时,分析结果知,获取数据的耗时基本全部在于取数据步骤,特征计算的耗时大部分在预测步骤。

根据以上分析可以确定,提升时序预测框架计算效率的关键点是:

  • 提升从数据库读取数据的效率;
  • 加快预测算法计算效率。

二、多进程

Python的多进程和多线程常用相应的库来实现,多进程使用Multiprocessing库、多线程用Threading库。那么选择多进程还是多线程的方法来优化框架呢?
对比多进程和多线程
对于python,存在特殊机制:为了保证数据安全,Python最常用的解释器Cpython中设定有全局解释器锁(Global Interpreter Lock, GIL)。GIL的作用是保证在一个进程中,同一时间只能有一个线程运行,其示意图如下(图片来自知乎)。所以Python的多线程实际上是一个进程内的多个线程之间频繁切换交替运行。根据官方文档的介绍,由于系统调度的原因,在多核处理器下执行多线程的计算,会比不使用多线程还要慢。

下图是stackoverflow上老哥的实验图,对比了python多进程和多线程的表现。
在这里插入图片描述
时间序列预测过程中有大量计算,属于CPU密集型任务,故应选择多进程并行方案。此外多进程还有以下优势:

  • 鲁棒性强,一个进程挂掉通常不会影响其它进程;
  • 编程和维护比较方便
  • 便于以分布式进程的方法进一步优化。

故选择使用多进程的思路优化框架。

测试发现,使用多进程并行方案后,用时减少了61.6%,效率提升很明显。

三、分布式进程

项目还有另一台服务器,干脆也拿过来练练手,学习下分布式啊集群啊啥的。

这台机器很干净,先装yum源:

rpm -qa | grep yum | xargs rpm -e --nodeps
wget http://mirrors.aliyun.com/centos/7.2.1511/os/x86_64/Packages/yum-metadata-parser-1.1.4-10.el7.x86_64.rpm
wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-3.4.3-168.el7.centos.noarch.rpm
wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-plugin-fastestmirror-1.1.31-54.el7_8.noarch.rpm
wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-utils-1.1.31-54.el7_8.noarch.rpm
wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/python-urlgrabber-3.10-10.el7.noarch.rpm
rpm -ivh python-urlgrabber-3.10-10.el7.noarch.rpm --force --nodeps
rpm -ivh yum-* --force --nodeps
cd /etc/yum.repos.d/
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
vi /etc/yum.repos.d/CentOS-Base.repo
:%s/$releasever/7/g
:wq
yum clean all
yum makecache
yum update

搭建项目的环境,部署项目代码等完成之后。按照以下思路实现分布式多进程:

  1. 在A 机器上建立队列Queue,用来进行进程间通信。服务进程创建任务队列task_queue 用来作为传递任务给任务进程的通道;服务进程创建结果队列result_queue ,作为任务进程完成任务后回复服务进程的通道。在分布式多进程环境下,必须由Queuemanager获得Queue 接口来添加任务;
  2. 把上一步建立的队列在网络上注册,暴露给其他进程(主机),注册后获得网络队列,相当于本地队列的映像;
  3. 建立一个对象(Queuemanager(BaseManager))实例manager,绑定端口和验证口令;
  4. 启动第三步中建立的实例,即启动管理manager,监管信息通道;
  5. 通过管理实例的方法获得通过网络访问的Queue对象,即再把网络队列实体化成可以使用的本地队列;
  6. 由ixjob调度A 机器上的定时任务,根据元数据表创建预测任务,启动服务进程将预测任务放入A 机器上的task队列中,自动上传任务到网络队列中,分配给A 和B 机器上的任务进程;
  7. 启动A 和B 机器上的任务进程接收任务进行处理,各自处理完成后将各自的预测结果插入至数据库,随后通过result_queue 传回一个标志成功/失败的结果。

完成后测试发现:框架的运行时间缩短77.5%,效果不错。


附录

进程:操作系统分配资源的最小单元,每个进程之间独立内存。内含一个或多个线程。

线程:系统调度的最小单元,一个进程内的各线程共享内存。

多进程:创建多个子进程,绕过Python的GIL,利用多个CPU并行计算。能够提升cpu密集型任务的运行效率。

多线程:对python的多进程来说,由于GIL(全局解释锁)的限制,同一时刻CPU执行的任务只有一个,CPU在不同的线程间切换执行。多线程能够提高IO密集型任务的效率。
IO密集型时,大部分线程都阻塞,多线程数:
参考公式:CPU核数 /(1 - 阻系数)
比如8核CPU:8/(1 - 0.9)=80个线程数
阻塞系数在0.8~0.9之间

CPU密集型:也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。
CPU密集型:核心线程数 = CPU核数 + 1

IO密集型:系统的CPU性能相对硬盘、内存要好很多,此时大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,CPU Loading并不高。I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。
IO密集型:核心线程数 = CPU核数 / (1-阻塞系数)
IO密集型:核心线程数 = CPU核数 * 2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值