NEP 15 — 合併 multiarray 與 umath#
- 作者:
Nathaniel J. Smith <njs@pobox.com>
- 狀態:
最終
- 類型:
標準追蹤
- 建立日期:
2018-02-22
- 決議:
https://mail.python.org/pipermail/numpy-discussion/2018-June/078345.html
摘要#
讓我們將 numpy.core.multiarray
和 numpy.core.umath
合併到單一擴充模組中,並棄用 np.set_numeric_ops
。
背景#
目前,numpy 的核心 C 程式碼被分為兩個獨立的擴充模組。
numpy.core.multiarray
是從 numpy/core/src/multiarray/*.c
建置而來,並包含核心陣列功能(特別是 ndarray
物件)。
numpy.core.umath
是從 numpy/core/src/umath/*.c
建置而來,並包含 ufunc 機制。
這兩個模組各自公開它們自己獨立的 C API,分別透過 import_multiarray()
和 import_umath()
存取。其概念是它們應該是獨立的模組,其中 multiarray
作為較低層級的層,而 umath
建構在其之上。但在實務上,這已被證明是有問題的。
首先,分層並不完美:當你寫 ndarray + ndarray
時,這會調用 ndarray.__add__
,然後它會呼叫 ufunc np.add
。這表示 ndarray
需要知道 ufuncs – 因此,我們沒有乾淨的分層,而是循環依賴。為了解決這個問題,multiarray
匯出了一個有點可怕的函式,稱為 set_numeric_ops
。每次你 import numpy
時的引導程序是
multiarray
及其ndarray
物件被載入,但 ndarray 上的算術運算會損壞。umath
被載入。set_numeric_ops
用於 monkeypatch 所有類似ndarray.__add__
的方法,使其使用來自umath
的物件。
此外,set_numeric_ops
作為公共 API np.set_numeric_ops
公開。
此外,即使這種分層確實有效,它最終也會扭曲我們公共 ABI 的形狀。近年來,將新函式添加到 multiarray
的「公共」ABI 的最常見原因,並不是因為它們真的需要是公共的,或者我們期望其他專案使用它們,而是因為我們需要從 umath
呼叫它們。這是非常不幸的,因為它使我們的公共 ABI 不必要地龐大,而且由於我們永遠無法從中移除東西,因此這造成了持續的維護負擔。C 語言的運作方式是,你可以擁有對同一擴充模組內的所有內容可見的內部 API,或者你可以擁有每個人都可以使用的公共 API;你不能(輕易地)擁有一個對 numpy 內的多個擴充模組可見,但對外部使用者不可見的 API。
我們也越來越多地將實用程式碼放入 numpy/core/src/private/
中,現在其中包含一堆檔案,這些檔案被 #include
了兩次,一次到 multiarray
中,一次到 umath
中。這非常糟糕,而且純粹是為了這些獨立的 C 擴充功能而採取的變通方案。npymath
程式庫也包含在這兩個擴充模組中。
提議的變更#
此 NEP 提議三項變更
我們應該開始將
numpy/core/src/multiarray/*.c
和numpy/core/src/umath/*.c
一起建置到單一擴充模組中。我們應該使用一些新的私有 API 來設定
ndarray.__add__
和相關功能,而不是使用set_numeric_ops
。我們應該棄用,並最終移除
np.set_numeric_ops
。
未提議的變更#
我們不一定提議要丟棄 multiarray/ 和 umath/ 在原始碼組織方面的區別:內部組織是有用的!我們只是想將它們一起建置到單一擴充模組中。當然,這確實為未來可能的重構打開了大門,然後我們可以根據它們出現時的優點來評估它們。
這也不表示我們將破壞公共 C ABI。我們應該繼續提供 import_multiarray()
和 import_umath()
函式 – 只是現在這兩個 ABI 最終將從同一個 C 程式庫載入。由於 import_multiarray()
和 import_umath()
的撰寫方式,我們仍然需要有稱為 numpy.core.multiarray
和 numpy.core.umath
的模組,而且它們需要繼續匯出 _ARRAY_API
和 _UFUNC_API
物件 – 但我們可以將其中一個或兩個模組變成微小的墊片,它們只是從實際定義它們的任何位置重新匯出神奇的 API 物件。(請參閱 numpy/core/code_generators/generate_{numpy,ufunc}_api.py
以了解這些匯入如何運作的詳細資訊。)
向後相容性#
唯一的相容性中斷是棄用 np.set_numeric_ops
。
拒絕的替代方案#
保留 set_numeric_ops
以進行 monkeypatching#
在討論此 NEP 時,針對 set_numeric_ops
提出了一個額外的用例:如果你有一個優化的向量數學程式庫(例如 Intel 的 MKL VML、Sleef 或 Yeppp),則可以使用 set_numeric_ops
來 monkeypatch numpy,以使用這些運算而不是 numpy 的內建向量運算。但是,即使我們承認這是一個很棒的想法,使用 set_numeric_ops
並不是實際上的最佳方法。所有 set_numeric_ops
允許你做的是接管 Python 在 ndarray 上的語法運算子(+
、*
等);它不讓你影響透過其他 API 呼叫的運算(例如 np.add
),或沒有內建語法的運算(例如 np.exp
)。此外,你必須重新實作整個 ufunc 機制,而不僅僅是核心迴圈。另一方面,PyUFunc_ReplaceLoopBySignature API(於 2006 年新增)允許替換任意 ufunc 的內部迴圈。這既更簡單又更強大 – 例如,替換 np.add
的內部迴圈意味著你的程式碼將自動用於 ndarray + ndarray
以及直接呼叫 np.add
。因此,這似乎不是不棄用 set_numeric_ops
的好理由。
討論#
版權#
本文件已被置於公共領域。