[英]Why is the float * int multiplication faster than int * float in CPython?
基本上,表達式0.4 * a
始終且令人驚訝地明顯快於a * 0.4
。 a
是 integer。 我不知道為什么。
我推測這是一個LOAD_CONST LOAD_FAST
字節碼對比 LOAD_FAST LOAD_CONST “更專業”的LOAD_FAST LOAD_CONST
,我會對這個解釋完全滿意,除了這個怪癖似乎只適用於乘法變量類型不同的乘法。 (順便說一句,我曾經在github上找到的這個“字節碼指令對流行度排名”的鏈接已經找不到了,有人有鏈接嗎?)
無論如何,這里是微觀基准:
$ python3.10 -m pyperf timeit -s"a = 9" "a * 0.4"
Mean +- std dev: 34.2 ns +- 0.2 ns
$ python3.10 -m pyperf timeit -s"a = 9" "0.4 * a"
Mean +- std dev: 30.8 ns +- 0.1 ns
$ python3.10 -m pyperf timeit -s"a = 0.4" "a * 9"
Mean +- std dev: 30.3 ns +- 0.3 ns
$ python3.10 -m pyperf timeit -s"a = 0.4" "9 * a"
Mean +- std dev: 33.6 ns +- 0.3 ns
正如您所看到的 - 在浮動首先出現的運行(第 2 次和第 3 次)中 - 它更快。
所以我的問題是這種行為從何而來? 我 90% 確定它是 CPython 的實現細節,但我對 state 的低級指令不太熟悉。
這是 CPython 對BINARY_MULTIPLY
操作碼的實現。 它不知道編譯時的類型是什么,所以一切都必須在運行時計算出來。 不管a
和b
是什么, BINARY_MULTIPLY
最終都會調用a.__mul__(b)
。
當a
是 int 類型時, int.__mul__(a, b)
不知道該怎么做,除非b
也是 int 類型。 它返回Py_RETURN_NOTIMPLEMENTED
(一個內部 C 常量)。 這是在longobject.c
的CHECK_BINOP
宏中。 解釋器對此進行了分析,並有效地說“好的, a.__mul__
不知道該做什么,所以讓我們b.__rmul__
”。 這些都不是免費的——這一切都需要時間。
float.__mul__(b, a)
(與float.__rmul__
相同)確實知道如何處理 int (首先將其轉換為 float),因此成功。
但是當a
開始是 float 類型時,我們 go 先到float.__mul__
,到此結束。 沒有時間花時間弄清楚 int 類型不知道該做什么。
實際的代碼比上面假裝的要復雜得多,但這就是它的要點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.