2014-11-03 52 views
9
from timeit import Timer as T 

def calc(n): 
    return T("class CLS(object): pass").timeit(n) 

print(calc(90000)) 
print(calc(90000)) 
print(calc(90000)) 

# python3.4 
1.1714721370008192 
1.0723806529986177 
1.111804607000522 

# python2.7 
15.7533519268 
16.7191421986 
16.8397979736 

爲什麼會出現使用不同版本的Python類創建時間如此大的差別? 測試在同一臺機器上:爲什麼有在Python 2.7和3.4蟒性能創建類之間的差別

  • i5-3450 CPU @ 3.10GHz
  • 8GB內存
+1

與此相關的:http://stackoverflow.com/q/10072428/3001761 – jonrsharpe 2014-11-03 11:55:03

+1

我想象的對象有什麼用它做 – 2014-11-03 11:58:45

+4

我已經收窄的問題降到'timeit'調用'gc.disable() '。由於類總是創建大量循環,禁用收集器意味着每個'CLS'實例都不能被釋放。你可以通過調用'gc.get_objects()'來獲得被跟蹤對象的列表。如果您不禁用GC,但性能相似,但只需將每個「CLS」實例附加到列表以保持引用。看起來這就是爲什麼在這種情況下,對於Python 2,'_PyObject_GC_Malloc'表現得更糟。 – eryksun 2014-11-04 03:52:38

回答

0

嗯,這個問題似乎是與舊式與新式類在Python 2.7。

在蟒蛇3.4,你可以看到,使用對象,而不是使用它的區別是符號的只是加載(不是顯著):

C:\TEMP>C:\Python34\python.exe 
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 
>>> def a(): 
... class A(object): pass 
... 
>>> def b(): 
... class B(): pass 
... 
>>> import dis 
>>> dis.dis(a) 
    2   0 LOAD_BUILD_CLASS 
       1 LOAD_CONST    1 (<code object A at 0x020B8F20, file "<stdin>", line 2>) 
       4 LOAD_CONST    2 ('A') 
       7 MAKE_FUNCTION   0 
      10 LOAD_CONST    2 ('A') 
      13 LOAD_GLOBAL    0 (object) # Extra step, not that expensive. 
      16 CALL_FUNCTION   3 (3 positional, 0 keyword pair) 
      19 STORE_FAST    0 (A) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE 
>>> dis.dis(b) 
    2   0 LOAD_BUILD_CLASS 
       1 LOAD_CONST    1 (<code object B at 0x020B8D40, file "<stdin>", line 2>) 
       4 LOAD_CONST    2 ('B') 
       7 MAKE_FUNCTION   0 
      10 LOAD_CONST    2 ('B') 
      13 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      16 STORE_FAST    0 (B) 
      19 LOAD_CONST    0 (None) 
      22 RETURN_VALUE 
>>> 

雖然關於Python 2.7,你有另外一個步驟包括LOAD_TUPLE:

C:\Users\jsargiot\Downloads\so>C:\Python27\python.exe 
Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 
>>> def a(): 
... class A(object): pass 
... 
>>> def b(): 
... class B(): pass 
... 
>>> import dis 
>>> dis.dis(a) 
    2   0 LOAD_CONST    1 ('A') 
       3 LOAD_GLOBAL    0 (object) # First extra step (just like 3.4) 
       6 BUILD_TUPLE    1   # Second extra step, expensive 
       9 LOAD_CONST    2 (<code object A at 01EAEA88, file "<stdin>", line 2>) 
      12 MAKE_FUNCTION   0 
      15 CALL_FUNCTION   0 
      18 BUILD_CLASS 
      19 STORE_FAST    0 (A) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE 
>>> dis.dis(b) 
    2   0 LOAD_CONST    1 ('B') 
       3 LOAD_CONST    3 (()) 
       6 LOAD_CONST    2 (<code object B at 01EB8EC0, file "<stdin>", line 2>) 
       9 MAKE_FUNCTION   0 
      12 CALL_FUNCTION   0 
      15 BUILD_CLASS 
      16 STORE_FAST    0 (B) 
      19 LOAD_CONST    0 (None) 
      22 RETURN_VALUE 
>>> 
2

timeit禁用垃圾收集器,否則會打破保持一個類的對象活着的週期。因此,在timeit完成之前,沒有任何類會被釋放。

object.__subclasses__()通過弱引用的內部集合引用這些類。舊的基於列表的tp_subclasses的實現每次搜索整個列表以找到可替換的死亡引用。這個過程需要更多的時間與每個附加的子類。另一方面,3.4中新的基於字典的設計可以在不斷的時間內添加參考。請參閱issue 17936


感謝@MichaelYounkin指點如何這也是3.2慢。最初我試圖縮小性能差異,以便在2.x和3.x之間改變小對象分配器,但在閱讀他的評論後,我發現3.3甚至比3.4慢很多。所以我掃描了typeobject.c filelog來回顧最近的變化。

相關問題