PyPy on Hadoop

在Hadoop Streaming中使用PyPy

PyPy作为CPython的高性能替代方案,目前已经十分成熟,得益于JIT等技术的应用,多数场景下PyPy都具有更好的性能。以通过正则对Nginx日志进行解析为例,对300G数据进行处理,PyPy对比原生Python能得到2.5倍以上的性能提升:

|            | Slot time(mins) | Read(%) | Write(%) | User(%) | MB/s | Record/s |
| ---------- | --------------- | ------- | -------- | ------- | ---- | -------- |
| Python 2.6 | 2412.76         | 3.17%   | 32.21%   | 64.61%  | 2.07 | 1741.74  |
| PyPy 2.3   | 869.37          | 9.01%   | 87.13%   | 3.85%   | 5.69 | 4809.06  |

在MapReduce任务中,使用官方支持的Portable PyPy能够比较完美地解决对运算性能以及运行环境的灵活配置的要求,该版本在CentOS 5上编译完成,对运行环境系统/链接库的要求很低,一般现在的RHEL/Centos/Ubuntu/Debian系统都能直接运行:

% ldd pypy
linux-vdso.so.1 =>  (0x00007fff959ff000)
libdl.so.2 => /lib64/libdl.so.2 (0x000000392e200000)
libm.so.6 => /lib64/libm.so.6 (0x000000302fe00000)
libz.so.1 => /lib64/libz.so.1 (0x00000039fe600000)
librt.so.1 => /lib64/librt.so.1 (0x000000381a600000)
libbz2.so.1 => /lib64/libbz2.so.1 (0x0000003031e00000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x000000392e600000)
libutil.so.1 => /lib64/libutil.so.1 (0x0000003032a00000)
libncurses.so.5 => /lib64/libncurses.so.5 (0x0000003270600000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003031200000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x000000302fa00000)
libc.so.6 => /lib64/libc.so.6 (0x000000302f600000)
/lib64/ld-linux-x86-64.so.2 (0x000000302ee00000)
libfreebl3.so => /lib64/libfreebl3.so (0x000000392ee00000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003030e00000)

将下载回来的PyPy包缓存到HDFS后,通过在MapReduce Streaming中指定archives/cacheArchive,即可在任务节点直接使用PyPy执行代码:

hadoop jar /home/etl/jar/hadoop-0.20.1.12-fb-streaming.jar
    -archives lib.zip 
    -D mapred.job.name=nobody-pypy-test 
    -D mapred.cache.archives=hdfs:///home/etl/archives/pypy.tar.gz#local
    -input /home/etl/DEMO/in
    -output /home/etl/DEMO/out
    -file bootstrap.py
    -mapper "./local/pypy/bin/pypy bootstrap.py"

上面例子中,mapred.cache.archives参数的#local指定了文件在节点工作目录下的访问路径,需要注意pypy.tar.gz的目录结构,若文件解压后得到的是pypy文件夹,则需要在访问时加上这层目录,如./local/pypy/bin/pypy

使用PyPy基本不需要修改原来的Python代码,具体可查看官方兼容性列表。另外一个好处是我们可以在本地安装上各种第三方库后再将文件打包上传,这样节点上就可以直接使用这些第三方库而不受系统限制(含C扩展的需要注意编译系统与节点系统一致),也间接实现了Virtualenv的功能(不同包中安装不同的库、使用不同的版本)。以使用增强的正则库regex为例,将PyPy解压到本地后:

wget https://bootstrap.pypa.io/get-pip.py .
/PATH/TO/PYPY/bin/pypy get-pip.py
/PATH/TO/PYPY/bin/pip install regex

然后再重新将PyPy打包并上传到HDFS:

cd /PATH/TO/PYPY
tar czf pypy.tar.gz pypy
hadoop fs -put pypy.tar.gz /home/etl/archives/

MapReduce时指定该包即可在节点上调用regex库。

PS:
概括地讲,代码逻辑越复杂(计算密集)使用PyPy得到的性能提升越大,但对简单的ETL任务,譬如sys.stdout.write('A B 123 C'.split()[2]),PyPy的表现可能会低于原生Python。上线前应该使用两种方式跑下测试数据,根据结果选择适合自己业务场景的方案。