什么是pypy
簡單的說,pypy 基于jit靜態(tài)編譯,相比cpython 動態(tài)解釋執(zhí)行,因此執(zhí)行速度上會更高效,同時減少了內(nèi)存使用。
對三方包的支持一直是pypy的軟肋,特別是一些科學(xué)計算包,不過在最新的 pypy5.9 中終于對Pandas和NumPy提供了支持。
一個簡單的例子:
test1:
import time
t = time.time()
i = 0
for i in xrange(10**8):
continue
print time.time() - t
test2:
import time
t = time.time()
i = 0
for i in xrange(10**8):
i = i + 1
print time.time() - t
case | pypy | Cpython |
---|---|---|
test1 | 0.25s | 4.3s |
test2 | 0.25s | 10s |
tips:
不難發(fā)現(xiàn),在 pure python 的測試中,一些場景會有幾十倍的性能提升。
不過在Pandas和NumPy的性能測試中,發(fā)現(xiàn)pypy會比Cpython慢4x-5x。
可以使用Numpypy替代NumPy,性能又能得到提升:
原因參考:https://morepypy.blogspot.com/2017/10/how-to-make-your-code-80-times-faster.html
PySpark
在python driver端,SparkContext利用Py4J啟動一個JVM并產(chǎn)生一個JavaSparkContext
RDD在python下的轉(zhuǎn)換會被映射成java環(huán)境下PythonRDD。在遠端worker機器上,PythonRDD對象啟動一些子進程并通過pipes與這些子進程通信。
使用 pypy 則是將與SparkWorker通信的Cpython進程替換成pypy進程。
pypy on PySpark
可以在 Spark-env.sh 中設(shè)置 export PYSPARK_PYTHON =/path/to/pypy
或者提交程序時指定--conf spark.pyspark.python=/path/to/pypy
等方式進行提交。
測試代碼:
//filter
rdd.filter(lambda x:x['addr'] != 'beijing')
//map
import re
def simpleMobileVerify(phone):
p2 = re.compile('^0\d{2,3}\d{7,8}$|^1[358]\d{9}$|^147\d{8}')
phonematch = p2.match(phone)
if(phone):
return phone
else:
return None
rdd.map(lambda x:simpleMobileVerify(x['accountMobile'])).filter(lambda x : x != None)
case | pypy | Cpython |
---|---|---|
filter | 60s | 67s |
map | 11s | 22s |
在filter這種IO密集型的任務(wù)中提升不大,在計算密集型的任務(wù)中提升較為明顯,提升比例與計算復(fù)雜度成正相關(guān)。
下圖為一個計算指標(biāo)任務(wù)的執(zhí)行時間,其中紅框部分使用pypy調(diào)度:
結(jié)論:
在真實的pySpark任務(wù)中,根據(jù)不同類型的任務(wù)提升幅度不同,可以根據(jù)不同的業(yè)務(wù)場景以及使用的三方包,使用Cpython和pypy。
其他的性能對比可以參考:
http://emptypipes.org/2015/01/17/python-vs-scala-vs-spark/