2013-02-22 114 views
4

我需要從特定的面/頂點列表中計算最小值,最大值和平均值。我試圖用Numpy優化這個計算,但沒有成功。純Python比Numpy更快嗎?我可以使這個numpy代碼更快嗎?

這是我的測試用例:

#!/usr/bin/python 
# -*- coding: iso-8859-15 -*- 
''' 
Module Started 22 févr. 2013 
@note: test case comparaison numpy vs python 
@author: Python4D/damien 
''' 

import numpy as np 
import time 


def Fnumpy(vertices): 
    np_vertices=np.array(vertices) 
    _x=np_vertices[:,:,0] 
    _y=np_vertices[:,:,1] 
    _z=np_vertices[:,:,2] 
    _min=[np.min(_x),np.min(_y),np.min(_z)] 
    _max=[np.max(_x),np.max(_y),np.max(_z)] 
    _mean=[np.mean(_x),np.mean(_y),np.mean(_z)] 
    return _mean,_max,_min 

def Fpython(vertices): 
    list_x=[item[0] for sublist in vertices for item in sublist] 
    list_y=[item[1] for sublist in vertices for item in sublist] 
    list_z=[item[2] for sublist in vertices for item in sublist] 
    taille=len(list_x) 
    _mean=[sum(list_x)/taille,sum(list_y)/taille,sum(list_z)/taille] 
    _max=[max(list_x),max(list_y),max(list_z)] 
    _min=[min(list_x),min(list_y),min(list_z)]  
    return _mean,_max,_min 

if __name__=="__main__": 
    vertices=[[[1.1,2.2,3.3,4.4]]*4]*1000000 
    _t=time.clock() 
    print ">>NUMPY >>{} for {}s.".format(Fnumpy(vertices),time.clock()-_t) 
    _t=time.clock() 
    print ">>PYTHON>>{} for {}s.".format(Fpython(vertices),time.clock()-_t) 

的結果是:

numpy的:

([1.1000000000452519,2.2000000000905038,3.3000000001880174],[1.1000000000000001,2.2000000000000002,3.2999999999999998 ],[1.1000000000000001,2.2000000000000002,3.2999999999999998])爲27.327068618s。

的Python:

([1.100000000045252,2.200000000090504,3.3000000001880174],[1.1,2.2,3.3],[1.1,2.2,3.3])爲1.81366938593s。

純Python的速度比Numpy快15倍!

+5

此行速度較慢,爲'np_vertices = np.array(頂點)'。你並不是真正計算最小和最大函數的時間,你需要花費多長時間來整理嵌套的引用 – YXD 2013-02-22 14:36:16

+0

你應該編輯你的問題,使我認爲是你隱含的問題,「我可以使這個numpy代碼更快?「,明確表示接近選票的意圖。 – tacaswell 2013-02-22 14:38:41

+0

通過僅使用numpy構造(也用於構建'頂點'),您可以大大加快代碼的速度。 – 2013-02-24 07:08:38

回答

10

你的Fnumpy較慢的原因是它包含一個不由Fpython完成的額外步驟:在內存中創建一個numpy數組。如果招行np_verticies=np.array(verticies)Fnumpy外部和定時部分的結果將有很大的不同:

>>NUMPY >>([1.1000000000452519, 2.2000000000905038, 3.3000000001880174], [1.1000000000000001, 2.2000000000000002, 3.2999999999999998], [1.1000000000000001, 2.2000000000000002, 3.2999999999999998]) for 0.500802s. 
>>PYTHON>>([1.100000000045252, 2.200000000090504, 3.3000000001880174], [1.1, 2.2, 3.3], [1.1, 2.2, 3.3]) for 2.182239s. 

您還可以通過提供的數據類型的提示,當您創建它numpy的顯著加快分配步驟。如果你告訴Numpy你有一組浮點數,那麼即使你在定時循環中保持np.array()的調用,它也會打敗純Python版本。

如果我改變np_vertices=np.array(vertices)np_vertices=np.array(vertices, dtype=np.float_)保持在Fnumpy,在Fnumpy版本將擊敗Fpython即使它有做了很多工作:

>>NUMPY >>([1.1000000000452519, 2.2000000000905038, 3.3000000001880174], [1.1000000000000001, 2.2000000000000002, 3.2999999999999998], [1.1000000000000001, 2.2000000000000002, 3.2999999999999998]) for 1.586066s. 
>>PYTHON>>([1.100000000045252, 2.200000000090504, 3.3000000001880174], [1.1, 2.2, 3.3], [1.1, 2.2, 3.3]) for 2.196787s. 
+0

我試過np_vertices = np.array(頂點,dtype = np.float_)或np_vertices = np.array(頂點,dtype = np.half),但沒有提升... >> NUMPY >>([inf, inf,inf],[1.0996,2.1992,3.3008],[1.0996,2.2992,3.3008])爲27.5570968929s。 >> PYTHON >>([1.100000000045252,2.200000000090504,3.3000000001880174],[1.1,2.2,3.3],[1.1,2.2,3.3])爲1.80307082548s。 – baco 2013-02-22 15:42:20

+0

你確定嗎?因爲從我的結果中可以看出,我看到了巨大的改進。如果有問題,請使用numpy 1.5.1和Python 2.7.1。 – 2013-02-22 15:49:13

+0

雖然這裏的重點在於你的numpy數組應該被創建/分配一次,並且儘可能重用,而不是在計算函數中重新創建。內存分配也需要很長時間,並且應該在任何算法中考慮 - 這不僅僅是計算限制了程序速度。 – 2013-02-22 15:51:57

2

正如所指出的其他人,你的問題是從列表到數組的轉換。通過使用適當的numpy函數,你將擊敗Python。我修改程序的主要組成部分:

if __name__=="__main__": 
    _t = time.clock() 
    vertices_np = np.resize(np.array([ 1.1, 2.2, 3.3, 4.4 ], dtype=np.float64), 
          (1000000, 4, 4)) 
    print "Creating numpy vertices: {}".format(time.clock() - _t) 
    _t = time.clock() 
    vertices=[[[1.1,2.2,3.3,4.4]]*4]*1000000 
    print "Creating python vertices: {}".format(time.clock() - _t) 
    _t=time.clock() 
    print ">>NUMPY >>{} for {}s.".format(Fnumpy(vertices_np),time.clock()-_t) 
    _t=time.clock() 
    print ">>PYTHON>>{} for {}s.".format(Fpython(vertices),time.clock()-_t) 

與我的機器在體改主要部分結果運行代碼:

Creating numpy vertices: 0.6 
Creating python vertices: 0.01 
>>NUMPY >>([1.1000000000452519, 2.2000000000905038, 3.3000000001880174], 
[1.1000000000000001, 2.2000000000000002, 3.2999999999999998], [1.1000000000000001, 
2.2000000000000002, 3.2999999999999998]) for 0.5s. 
>>PYTHON>>([1.100000000045252, 2.200000000090504, 3.3000000001880174], [1.1, 2.2, 3.3], 
[1.1, 2.2, 3.3]) for 1.91s. 

雖然數組創建仍與numpy的工具爲略長使用python的列表乘法運算符(0.6s對0.01s)創建嵌套列表,可以獲得ca的一個因子。 4代表您的代碼的運行時相關部分。如果我更換行:

np_vertices = np.asarray(vertices) 

避免大陣列的複製

np_vertices=np.array(vertices) 

,該numpy的功能的運行時間甚至下降到0.37s我的機器上,比多比純Python版本快5倍。

在你真實的代碼中,如果你事先知道頂點的數量,你可以通過np.empty()預先分配合適的數組,然後用合適的數據填充它,並將它傳遞給函數的numpy版本。