忻州網(wǎng)站建設(shè)一條龍全包(rust編程)rust pytorch,
原標(biāo)題:讓 Python 速度提高 100 倍,只需不到 100 行 Rust 代碼!不少程序員都抱怨 Python 代碼跑的慢,尤其是當(dāng)處理的數(shù)據(jù)集比較大的時(shí)候?qū)Υ耍疚淖髡咧赋觯褐恍璨坏?100 行 Rust 代碼就能解決這個(gè)問(wèn)題。
原文鏈接:https://ohadravid.github.io/posts/2023-03-rusty-python/作者| Ohad Ravid譯者 | 彎月 責(zé)編 | 鄭麗媛 出品 | CSDN(ID:CSDNn
ews)最近,我們的一個(gè)核心 Python 庫(kù)遇到了性能問(wèn)題這是一個(gè)非常龐大且復(fù)雜的庫(kù),是我們 3D 處理管道的支柱,使用了 NumPy 以及其他 Python 數(shù)據(jù)科學(xué)庫(kù)來(lái)執(zhí)行各種數(shù)學(xué)和幾何運(yùn)算具體來(lái)說(shuō),我們的系統(tǒng)必須在 CPU 資源有限的情況下在本地運(yùn)行,雖然起初的性能還不錯(cuò),但隨著并發(fā)用戶(hù)數(shù)量的增長(zhǎng),我們開(kāi)始遇到問(wèn)題,系統(tǒng)也出現(xiàn)了超負(fù)載。
我們得出的結(jié)論是:系統(tǒng)至少需要再快 50 倍才能處理這些增加的工作負(fù)載——而我們認(rèn)為,Rust 可以幫助我們實(shí)現(xiàn)這一目標(biāo)因?yàn)槲覀冇龅降男阅軉?wèn)題很常見(jiàn),所以 下面,我來(lái)簡(jiǎn)單介紹一下解決過(guò)程:(a)基本的潛在問(wèn)題;。
(b)我們可以通過(guò)哪些優(yōu)化來(lái)解決這個(gè)問(wèn)題。
我們的運(yùn)行示例首先,我們通過(guò)一個(gè)小型庫(kù)來(lái)展示最初的性能問(wèn)題假設(shè)有一個(gè)多邊形列表和一個(gè)點(diǎn)列表,且都是二維的,出于業(yè)務(wù)需求,我們需要將每個(gè)點(diǎn)“匹配”到一個(gè)多邊形我們的庫(kù)需要完成下列任務(wù):? 從點(diǎn)和多邊形的初始列表(全部為 2D)著手。
? 對(duì)于每個(gè)點(diǎn),根據(jù)與中心的距離,找到離點(diǎn)最近的多邊形的子集 ? 從這些多邊形中,選擇一個(gè)“最佳”多邊形 代碼大致如下:fromtyping importList, Tuple importnumpy 。
asnp fromdataclasses importdataclass fromfunctools importcached_property Point = np.array@dataclassclass
Polygon: x: np.arrayy: np.array@cached_propertydefcenter(self)-> Point: ... defarea(self)-> float: ...
deffind_close_polygons(polygon_subset: List[Polygon], point: Point, max_dist: float)-> List[Polygon]:
...defselect_best_polygon(polygon_sets: List[Tuple[Point, List[Polygon]]])-> List[Tuple[Point, Polygon]]:
...defmain(polygons: List[Polygon], points: np.ndarray)-> List[Tuple[Point, Polygon]]: ...性能方面最主要的難點(diǎn)在于,Python 對(duì)象和 numpy 數(shù)組的混合。
下面,我們簡(jiǎn)單地分析一下這個(gè)問(wèn)題需要注意的是,對(duì)于上面這段代碼,我們當(dāng)然可以把一切都轉(zhuǎn)化成 numpy 的向量計(jì)算,但真正的庫(kù)不可能這么做,因?yàn)檫@會(huì)導(dǎo)致代碼的可讀性和可修改性大大降低,收益也非常有限此外,使用
任何基于JIT 的技巧(PyPy / numba)產(chǎn)生的收益都非常小為什么不直接使用 Rust 重寫(xiě)所有代碼?雖然重寫(xiě)所有代碼很誘人,但有一些問(wèn)題:? 該庫(kù)的大量計(jì)算使用了 numpy,Rust 也不一定能提高性能。
? 該庫(kù)龐大而復(fù)雜,關(guān)系到核心業(yè)務(wù)邏輯,而且高度算法化,因此重寫(xiě)所有代碼需要付出幾個(gè)月的努力,而我們可憐的本地服務(wù)器眼看就要掛了 ? 一群好心的研究人員積極努力改進(jìn)這個(gè)庫(kù),實(shí)現(xiàn)了更好的算法,并進(jìn)行了大量實(shí)驗(yàn)。
他們不太愿意學(xué)習(xí)一門(mén)新的編程語(yǔ)言,而且還要等待編譯,還要研究復(fù)雜的借用檢查器——他們不希望離開(kāi)舒適區(qū)太遠(yuǎn) 小心探索下面,我來(lái)介紹一下我們的分析器Python 有一個(gè)內(nèi)置的 Profiler (cProfile),但對(duì)于我們來(lái)說(shuō),選擇這個(gè)工具不太合適:。
?它會(huì)為所有 Python 代碼引入大量開(kāi)銷(xiāo),卻不會(huì)給原生代碼帶來(lái)額外開(kāi)銷(xiāo),因此測(cè)試結(jié)果可能有偏差 ?我們將無(wú)法查看原生代碼的調(diào)用幀,這意味著我們也無(wú)法查看 Rust 代碼 所以,我們計(jì)劃使用 py-spy,它。
是一個(gè)采樣分析器,可以查看原生幀他們還將預(yù)構(gòu)建的輪子發(fā)布到了 pypi,因此我們只需運(yùn)行 pip install py-spy 即可此外,我們還需要一些測(cè)量指標(biāo)# measure.pyimport time。
import poly_matchimport os# Reduce noise, actually improve perf in our case.os.environ[ "OPENBLAS_NUM_THREADS"
] = "1"polygons, points = poly_match.generate_example# We are going to increase this as the code gets faster and faster.
NUM_ITER = 10t0 = time.perf_counterfor _ in range(NUM_ITER):poly_match.main(polygons, points)t1 = time.perf_counter
took = (t1 - t0) / NUM_ITERprint(f "Took and avg of {took * 1000:.2f}ms per iteration") 這些測(cè)量指標(biāo)雖然不是很科學(xué),但可以幫助我們優(yōu)化性能。
“我們很難找到合適的測(cè)量基準(zhǔn)但請(qǐng)不要過(guò)分強(qiáng)調(diào)擁有完美的基準(zhǔn)測(cè)試設(shè)置,尤其是當(dāng)你優(yōu)化某個(gè)程序時(shí)”—— Nicholas Nethercote,《The Rust Performance Book》運(yùn)行該腳本,我們就可以獲得測(cè)量基準(zhǔn):。
$ python measure.pyTook an avg of293.41ms per iteration 對(duì)于原來(lái)的庫(kù),我們使用了 50 個(gè)不同的樣本來(lái)確保涵蓋所有情況這個(gè)測(cè)量結(jié)果與實(shí)際的系統(tǒng)性能相符,這意味著,我們的工作就是突破這個(gè)數(shù)字。
我們還可以使用 PyPy 進(jìn)行測(cè)量:$ conda create-n pypyenv -c conda-forge pypy numpy && conda activatepypyenv $ pypy measure_with_warmup.py
Took an avgof1495.81ms per iteration 先測(cè)量首先,我們來(lái)找出什么地方如此之慢$py-spy record --native -o profile.svg -- python measure.py 。
py-spy>Sampling process 100 timesa second. Press Control-C to exit. Took an avg of 365.43ms per iteration
py-spy>Stopped sampling because process exited py-spy>Wrote flamegraph data to profile.svg. Samples: 391 Errors: 0
我們可以看到開(kāi)銷(xiāo)非常小相較而言,使用 cProfile 得到的數(shù)據(jù)如下:$ python -m cProfile measure.pyTook an avg of546.47ms per iteration 。
7551778functioncalls( 7409483primitive calls ) in7.806 seconds…下面是我們獲得的火焰圖:
每個(gè)方框都是一個(gè)函數(shù),我們可以看到每個(gè)函數(shù)花費(fèi)的相對(duì)時(shí)間,包括它正在調(diào)用的函數(shù)(沿著圖形/棧向下)要點(diǎn)總結(jié):? 絕大部分時(shí)間花在 find_close_polygons 上? 大部分時(shí)間都花在執(zhí)行 norm,這是一個(gè) numpy 函數(shù)。
下面,我們來(lái)仔細(xì)看看 find_close_polygons:deffind_close_polygons(polygon_subset: List[Polygon], point: np.array, max_dist: float
)-> List[Polygon]: close_polygons = []forpoly inpolygon_subset: ifnp.linalg.norm(poly.center - point) < max_dist:
close_polygons.append(poly)returnclose_polygons 我們打算用 Rust 重寫(xiě)這個(gè)函數(shù)在深入細(xì)節(jié)之前,請(qǐng)務(wù)必注意以下幾點(diǎn):? 此函數(shù)接受并返回復(fù)雜對(duì)象(Polygon、np.array)。
? 對(duì)象的大小非常重要(因此復(fù)制需要一定的開(kāi)銷(xiāo)) ? 這個(gè)函數(shù)被調(diào)用了很多次(所以我們引入的開(kāi)銷(xiāo)可能會(huì)引發(fā)問(wèn)題) 我的第一個(gè) Rust 模塊PyO3 是一個(gè)用于 Python 和 Rust 之間交互的 crate ,擁有非常好的文檔。
我們將調(diào)用自己的 poly_match_rs,并添加一個(gè)名為 find_close_polygons 的函數(shù)mkdirpoly_match_rs && cd "$_"pipinstall maturin。
maturininit --bindings pyo3maturindevelop剛開(kāi)始的時(shí)候,我們的 crate 大致如下:use pyo3::prelude::*;#[py function] fn
find_close_polygons( ) -> PyResult { Ok()}#[pymodule]fn poly_match_rs(_py: Python, m: &PyModule) -> PyResult<> {
m.add_function(wrap_py function!( find_close_polygons, m)?)? ; Ok()}我們還需要記住,每次修改 Rust 庫(kù)時(shí)都需要執(zhí)行 maturin develop。
改動(dòng)就這么多下面,我們來(lái)調(diào)用新函數(shù),看看情況會(huì)怎樣>>> poly_match_rs.find_close_polygons( polygons, point, max_dist) ETypeError。
: poly_match_rs.poly_match_rs.find_close_polygonstakesnoarguments(3 given) 第一版:Rust 轉(zhuǎn)換首先,我們來(lái)定義 APIPyO3 可以幫助我們將 Python 轉(zhuǎn)換成 Rust:。
#[pyfunction]fn find_close_polygons( polygons:Vec, point:PyObject, max_dist:f64) -> PyResult
>> { Ok(vec![])}PyObject (顧名思義)是一個(gè)通用、“一切皆有可能”的 Python 對(duì)象稍后,我們將嘗試與它進(jìn)行交互這樣程序應(yīng)該就可以運(yùn)行了(盡管不正確)我直接把原來(lái)的 Python 函數(shù)復(fù)制粘帖進(jìn)去,并修復(fù)了語(yǔ)法問(wèn)題。
#[pyfunction]fn find_close_polygons( polygons: Vec, point: PyObject, max_dist: f64) -> PyResult
{ letmut close_polygons = vec![]; forpoly inpolygons { ifnorm( poly.center - point) < max_dist { close_polygons.push(poly)
}}Ok(close_polygons)}可惜未能通過(guò)編譯:% maturin develop...error[E0609]: no field `center`on type `Py`--> src/lib.
rs:8:22|8 |ifnorm(poly.center - point) < max_dist { | ^^^^^^ unknown fielderror[E0425]: cannot find function `norm`
inthis scope --> src/lib.rs:8:12|8| ifnorm(poly.center - point) < max_dist { |^^^^ notfound inthis scope
error:aborting due to 2previous errors ] 58/ 59: poly_match_rs 我們需要 3 個(gè) crate 才能實(shí)現(xiàn)函數(shù):# For Rust-native array operations.
ndarray= "0.15"# For a `norm` function for arrays.ndarray-linalg= "0.16"# For accessing numpy-created objects, based on `ndarray`.
numpy= "0.18"首先,我們將 point: PyObject 轉(zhuǎn)換成可以使用的東西我們可以利用 PyO3 來(lái)轉(zhuǎn)換 numpy 數(shù)組:use numpy::PyReadonlyArray1;#[py
function] fnfind_close_polygons( // An object which says "I have the GIL", so we can access Python-managed memory.
py: Python,polygons: Vec,// A reference to a numpy array we will be able to access.point: PyReadonlyArray1,
max_dist: f64,) -> PyResult< Vec> { // Convert to `ndarray::ArrayView1`, a fully operational native array.
letpoint = point.as_array; ...}現(xiàn)在 point 變成了 ArrayView1,我們可以直接使用了例如:// Make the `norm` function available.。
usendarray_linalg:: Norm; assert_eq!((point.to_owned - point).norm, 0.); 接下來(lái),我們需要獲取每個(gè)多邊形的中心,然后將其轉(zhuǎn)換成 ArrayView1。
letcenter = poly .getattr(py, "center")? // Python-style getattr, requires a GIL token (`py`)..extract::(py)?
// Tell PyO3 what to convert the result to..as_array // Like `point` before..to_owned; // We need one of the sides of the `-` to be "owned".
雖然信息量有點(diǎn)大,但總的來(lái)說(shuō),結(jié)果就是逐行轉(zhuǎn)換原來(lái)的代碼:usepyo3::prelude::*;usendarray_linalg::Norm;usenumpy::PyReadonlyArray1;#[pyfunction]
fnfind_close_polygons(py: Python,polygons: Vec,point: PyReadonlyArray1,max_dist: f64,
)-> PyResult {letmut close_polygons = vec![];letpoint = point.as_array;forpoly in polygons {
letcenter = poly.getattr(py,"center")?.extract: :(py)?.as_array.to_owned;if(center - point).norm < max_dist {
close_polygons.push(poly)}}Ok(close_polygons)}對(duì)比一下原來(lái)的代碼:deffind_close_polygons(polygon_subset: List[Polygon], point: np.array, max_dist: float
)-> List[Polygon]: close_polygons = []forpoly inpolygon_subset: ifnp.linalg.norm(poly.center - point) < max_dist:
close_polygons.append(poly)returnclose_polygons 我們希望這個(gè)版本優(yōu)于原來(lái)的函數(shù),但究竟有多少提升呢?$( cd./poly_match_rs/ && maturin develop)
$python measure.py Took an avg of 609.46ms per iteration看起來(lái) Rust 非常慢?實(shí)則不然,使用maturin develop --release運(yùn)行,就能獲得更好的結(jié)果:
$( cd./poly_match_rs/ && maturin develop --release) $python measure.py Took an avg of 23.44ms per iteration
這個(gè)速度提升很不錯(cuò)啊我們還想查看我們的原生代碼,因此發(fā)布時(shí)需要啟用調(diào)試符號(hào)即便啟用了調(diào)試,我們也希望看到最大速度# added to Cargo.toml[profile.release]debug=
true# Debug symbols for our profiler. lto= true# Link-time optimization. codegen-units= 1# Slower compilation but faster code.
第二版:用 Rust 重寫(xiě)更多代碼接下來(lái),在 py-spy 中通過(guò) --native 標(biāo)志,查看 Python 代碼與新版的原生代碼再次運(yùn)行 py-spy:$py-spy record --native -o profile.svg -- python measure.py 。
py-spy>Sampling process 100 timesa second. Press Control-C to exit. 這次得到的火焰圖如下所示(添加紅色之外的顏色,以方便參考):看看分析器的輸出,我們發(fā)現(xiàn)了一些有趣的事情:
1.find_close_polygons::...::trampoline(Python 直接調(diào)用的符號(hào))和__pyfunction_find_close_polygons(我們的實(shí)現(xiàn))的相對(duì)大小? 可以看到二者分別占據(jù)了樣本的 95% 和 88%,因此額外開(kāi)銷(xiāo)非常小。
2.實(shí)際邏輯(if (center - point).norm < max_dist { ... }) 是 lib_v1.rs:22(右側(cè)非常小的框),大約占總運(yùn)行時(shí)間的 9%? 所以應(yīng)該可以實(shí)現(xiàn) 10 倍的提升。
3.大部分時(shí)間花在 lib_v1.rs:16 上,它是 poly.getattr(...).extract(...),可以看到實(shí)際上只是 getattr 以及使用 as_array 獲取底層數(shù)組也就是說(shuō),我們需要專(zhuān)心解決第 3 點(diǎn),而解決方法是用 Rust 重寫(xiě) Polygon。
我們來(lái)看看目標(biāo)類(lèi):@dataclassclassPolygon: x:np.array y:np.array _area:float = None @cached_propertydefcenter(
self) -> np. array:centroid = np.array([ self.x, self.y]).mean(axis= 1) returncentroid defarea( self)
-> float:ifself._area is None:self._area = 0. 5* np.abs( np.dot( self.x, np.roll( self.y, 1)) - np.dot(
self.y, np.roll( self.x, 1)) )returnself._area 我們希望盡可能保留現(xiàn)有的 API,但我們不需要 area 的速度大幅提升實(shí)際的類(lèi)可能有其他復(fù)雜的東西,比如 merge 方法——使用了 scipy.spatial 中的 ConvexHull。
為了降低成本,我們只將 Polygon 的“核心”功能移至 Rust,然后從 Python 中繼承該類(lèi)來(lái)實(shí)現(xiàn) API 的其余部分我們的 struct 如下所示:// `Array1` is a 1d array, and the `numpy` crate will play nicely with it.。
use ndarray::Array1;// `subclass` tells PyO3 to allow subclassing this in Python.#[pyclass(subclass)]
structPolygon{ x: Array1,y: Array1,center: Array1,}下面,我們需要實(shí)現(xiàn)這個(gè) struct我們先公開(kāi) poly.{x, y, center},作為:。
? 屬性? numpy 數(shù)組 我們還需要一個(gè) constructor,以便 Python 創(chuàng)建新的 Polygon:usenumpy::{ PyArray1, PyReadonlyArray1, ToPyArray
}; #[pymethods]impl Polygon {#[new]fn new(x: PyReadonlyArray1, y: PyReadonlyArray1) -> Polygon {
let x = x.as_array;let y = y.as_array;let center = Array1::from_vec(vec![x.mean.unwrap, y.mean.unwrap]);
Polygon {x: x.to_owned,y: y.to_owned,center,}}// the `Py` in the return type is a way of saying "an Object owned by Python".
#[getter] fn x(& self, py: Python) -> PyResult
}// Same for `y` and `center`.}我們需要將這個(gè)新的 struct 作為類(lèi)添加到模塊中:#[pymodule]fn poly_match_rs(_py: Python, m: &PyModule) -> PyResult<> {
m.add_class::?; // new.m.add_function(wrap_py function!( find_close_polygons, m)?)? ; Ok()}然后更新 Python 代碼:
classPolygon(poly_match_rs.Polygon): _area: float = Nonedefarea(self)-> float: ...下面,編譯代碼——雖然可以運(yùn)行,但速度非常慢!
為了提高性能,我們需要從 Python 的 Polygon 列表中提取基于 Rust 的 PolygonPyO3 可以非常靈活地處理這類(lèi)操作,所以我們可以通過(guò)幾種方法來(lái)完成我們有一個(gè)限制是我們還需要返回 Python 的 Polygon,而且我們不想克隆任何實(shí)際數(shù)據(jù)。
我們可以針對(duì)每個(gè) PyObject 調(diào)用 .extract::(py)?,但也可以要求 PyO3 直接給我們 Py這是對(duì) Python 擁有的對(duì)象的引用,我們希望它包含原生 pyclass 結(jié)構(gòu)的實(shí)例(或子類(lèi),在我們的例子中)。
#[py function] fnfind_close_polygons( py: Python,polygons: Vec, // References to Python-owned objects.
point: PyReadonlyArray1,max_dist: f64,) -> PyResult< Vec< Py>> { // Return the same `Py` references, unmodified.
letmut close_polygons = vec![]; letpoint = point.as_array; forpoly inpolygons { letcenter = poly.borrow(py).center
// Need to use the GIL (`py`) to borrow the underlying `Polygon`..to_owned;if(center - point).norm < max_dist {
close_polygons.push(poly)}}Ok(close_polygons)}下面,我們來(lái)看看使用這些代碼的效果如何:$ python measure.pyTook an avg of6.29
ms per iteration 我們快要成功了,只需再提升一倍的速度即可第三版:避免內(nèi)存分配我們?cè)賮?lái)看一看分析器的結(jié)果1.首先,我們看到 select_best_polygon,現(xiàn)在它調(diào)用的是一些 Rust 代碼(在獲取 x 和 y 向量時(shí))。
? 我們可以解決這個(gè)問(wèn)題,但這是一個(gè)非常小的提升(大約為 10%)2.我們看到 extract_argument 花費(fèi)了大約 20% 的時(shí)間(在 lib_v2.rs:48 下),這個(gè)開(kāi)銷(xiāo)相對(duì)比較大? 但大部分時(shí)間都花在了 PyIterator::next 和 PyTypeInfo::is_type_of 中,這可不容易修復(fù)。
3.我們看到大量時(shí)間花在了內(nèi)存分配上? lib_v2.rs:58 是我們的 if 語(yǔ)句,我們還看到了drop_in_place和to_owned ? 實(shí)際的代碼大約占總時(shí)間的 35%,遠(yuǎn)超我們的預(yù)期所有數(shù)據(jù)都已存在,所以這一段本應(yīng)非??臁?/p>
下面,我們來(lái)解決最后一點(diǎn)有問(wèn)題的代碼如下:letcenter = poly.borrow(py).center .to_owned;if(center - point).norm < max_dist { ... } 。
我們希望避免 to_owned但是,我們需要一個(gè)已擁有的 norm 對(duì)象,所以我們必須手動(dòng)實(shí)現(xiàn)具體的寫(xiě)法如下:use ndarray_linalg::Scalar;let center = &poly.as_ref(py).borrow.center;。
if((center[ 0] - point[ 0]).square + (center[ 1] - point[ 1]).square). sqrt< max_dist { close_polygons.push(poly)
}然而,借用檢查器報(bào)錯(cuò)了:error[E0505]: cannot move out of `poly`because it is borrowed --> src/lib. rs:58:33|55 |
let center = &poly.as_ref(py).borrow.center; | ------------------------|||borrow of `poly`occurs here
| a temporary with access to the borrow is created here ......58 |close_polygons.push(poly); | ^^^^ move out of `poly` occurs here
59 |} 60| }|- ... andthe borrow might be used here, whenthat temporary is dropped andruns the `Drop`code
fortype `PyRef`借用檢查器是正確的,我們使用內(nèi)存的方式不正確更簡(jiǎn)單的修復(fù)方法是直接克隆,然后 close_polygons.push(poly.clone) 就可以通過(guò)編譯了這實(shí)際上是一個(gè)開(kāi)銷(xiāo)很低的克隆,因?yàn)槲覀冎辉黾恿?Python 對(duì)象的引用計(jì)數(shù)。
然而,在這個(gè)例子中,我們也可以通過(guò)一個(gè) Rust 的常用技巧:letnorm = { letcenter = &poly.as_ref(py).borrow.center; ((center[ 0] - point[
0]).square + (center[ 1] - point[ 1]).square).sqrt };ifnorm < max_dist { close_polygons.push(poly)}由于 poly 只在內(nèi)部范圍內(nèi)被借用,如果我們接近 close_polygons.pus,編譯器就可以知道我們不再持有引用,因此就可以通過(guò)編譯。
最后的結(jié)果:$ python measure.pyTook an avg of2.90ms per iteration 相較于原來(lái)的代碼,整體性能得到了 100 倍的提升總結(jié)我們?cè)瓉?lái)的 Python 代碼如下:。
@dataclassclassPolygon: x: np.arrayy: np.array_area: float = None@cached_propertydefcenter(self)-> np.array:
centroid = np.array([self.x, self.y]).mean(axis= 1) returncentroid defarea(self)-> float: ...deffind_close_polygons
(polygon_subset: List[Polygon], point: np.array, max_dist: float)-> List[Polygon]: close_polygons = []
forpoly inpolygon_subset: ifnp.linalg.norm(poly.center - point) < max_dist: close_polygons.append(poly)
returnclose_polygons # Rest of file (main, select_best_polygon).我們使用 py-spy 對(duì)其進(jìn)行了分析,即便用最簡(jiǎn)單的、逐行轉(zhuǎn)換的 find_close_polygons,也可以獲得 10 倍的性能提升。
我們反復(fù)進(jìn)行分析-修改代碼-測(cè)量結(jié)果,并最終獲得了 100 倍的性能提升,同時(shí) API 仍然保持與原來(lái)的庫(kù)相同最終得到的 Python 代碼如下:importpoly_match_rs frompoly_match_rs 。
importfind_close_polygons classPolygon(poly_match_rs.Polygon): _area: float = Nonedefarea(self)-> float:
...# Rest of file unchanged (main, select_best_polygon).調(diào)用的 Rust 代碼如下:usepyo3::prelude::*;usendarray::Array1;
usendarray_linalg::Scalar;usenumpy::{PyArray1, PyReadonlyArray1, ToPyArray};#[pyclass(subclass)]struct
Polygon {x: Array1,y: Array1,center: Array1,}#[pymethods]implPolygon {#[new]fnnew(x: PyReadonlyArray1, y: PyReadonlyArray1) -> Polygon {
letx = x.as_array;lety = y.as_array;letcenter = Array1::from_vec(vec![x.mean.unwrap, y.mean.unwrap]);
Polygon{x: x.to_owned,y: y.to_owned,center,}}#[getter]fnx(&self, py: Python) -> PyResult
Ok(self.x.to_pyarray(py).to_owned)}//Same for `y` and `center`.}#[pyfunction]fnfind_close_polygons(py
: Python,polygons: Vec,point: PyReadonlyArray1,max_dist: f64,)-> PyResult
letmut close_polygons = vec![];letpoint = point.as_array;forpoly in polygons {letnorm = {letcenter = &poly.as_ref(py).borrow.center;
((center[0]- point[0]).square + (center[1] - point[1]).square).sqrt};ifnorm < max_dist {close_polygons.push(poly)
}}Ok(close_polygons)}#[pymodule]fnpoly_match_rs(_py: Python, m: &PyModule) -> PyResult<> {m.add_class
: :?;m.add_function(wrap_pyfunction!(find_close_polygons,m)?)?;Ok()}要點(diǎn)總結(jié)? Rust(在 PyO3 的幫助下)能夠以非常小的代價(jià)換取 Python 代碼性能的大幅提升。
? 對(duì)于研究人員來(lái)說(shuō),Python API 非常優(yōu)秀,同時(shí)使用 Rust 快速構(gòu)建基本功能是一個(gè)非常強(qiáng)大的組合 ? 分析非常有趣,可以幫助你了解代碼中的一切 ?最希望ChatGPT開(kāi)源,一半開(kāi)發(fā)者參與過(guò)開(kāi)源貢獻(xiàn),63%的人在用愛(ài)發(fā)電|中國(guó)開(kāi)源開(kāi)發(fā)者現(xiàn)狀 。
? 微軟總裁稱(chēng)中國(guó)將是 ChatGPT 主要對(duì)手;曝蘋(píng)果 M3 芯片下半年量產(chǎn);Linux 6.3 正式發(fā)布|極客頭條 ? ChatGPT 搶不走程 序員飯碗的原因找到了?最新研究:它自動(dòng)生成了 21 個(gè)程序,16 個(gè)有漏
洞返回搜狐,查看更多責(zé)任編輯: