Rust是一種系統編程語言,可以快速運行,防止Segfaults並確保線程安全性。
特色
摘自:從rust-lang.org
我從Elias A成員和Rust Brazil Telegram Group的Rust Guru聽到的Rust的更好描述
Rust是一種允許您構建高級抽象的語言,但不放棄低級控制 - 也就是說,控制數據中如何表示數據,可以控制要使用的線程模型等。
Rust是一種通常可以在編譯過程中檢測到最壞的並行性和內存管理錯誤(例如在不同步的情況下訪問數據上的數據或使用數據後使用數據)的語言,但是如果您真的知道自己在做什麼,則可以為您提供距離逃脫。
Rust是一種語言,因為它沒有運行時,可用於與任何運行時集成;您可以在Rust中編寫一個本機擴展名,該擴展名是由程序Node.js或Python程序稱為Ruby,Lua等的程序。另一方面,您可以使用這些語言在Rust中腳本腳本腳本。 - “ Elias Gabriel Amaral Da Silva”

那裡有一堆生鏽包裹,可以幫助您用鏽蝕延伸Python。
我可以提到Armin Ronacher(燒瓶的創造者)創建的Milksnake,也可以提及Pyo3 Python解釋器的Rust Bindings
請參閱底部的完整參考列表。
對於這篇文章,我將使用Rust Cpython,它是我測試過的唯一一個,它與Rust穩定版本兼容,發現它很容易使用。
注意:Pyo3是Rust-Cpython的叉子,具有許多改進,但是只能與Rust的夜間版本一起使用,因此我更喜歡在此帖子中使用該穩定性,無論如何,這裡的示例也必須與Pyo3一起使用。
優點:編寫從Python進口的Rust功能和進口非常容易,正如您將以表現方面價值的基準所看到的那樣。
缺點:您的項目/LIB/框架的分佈將要求由於環境和架構的變化而在目標系統上編譯的Rust模塊,在安裝純Python庫時,您將沒有一個編譯階段,您可以使用Rust-etpeuptools或使用Mirksnake在Python Wheels中使用Mirksnake來更輕鬆。
是的,Python在某些情況下以“緩慢”而聞名,好消息是,這並不重要,具體取決於您的項目目標和優先事項。對於大多數項目,此細節不是很重要。
但是,您可能會面臨一個罕見的情況,即單個功能或模塊花費太多時間並且被檢測到項目性能的瓶頸,通常會發生在字符串解析和圖像處理中。
可以說,您具有執行某種字符串處理的Python函數,以以下簡單的示例來counting pairs of repeated chars但請注意,可以使用其他string processing功能或Python中任何其他一般緩慢的過程來複製此示例。
# How many subsequent-repeated group of chars are in the given string?
abCCdeFFghiJJklmnopqRRstuVVxyZZ... {millions of chars here}
1 2 3 4 5 6 Python的進行大型string處理非常慢,因此您可以使用pytest-benchmark來比較Pure Python (with Iterator Zipping)功能與Regexp實現。
# Using a Python3.6 environment
$ pip3 install pytest pytest-benchmark
然後編寫一個名為doubles.py的新Python程序
import re
import string
import random
# Python ZIP version
def count_doubles ( val ):
total = 0
for c1 , c2 in zip ( val , val [ 1 :]):
if c1 == c2 :
total += 1
return total
# Python REGEXP version
double_re = re . compile ( r'(?=(.)1)' )
def count_doubles_regex ( val ):
return len ( double_re . findall ( val ))
# Benchmark it
# generate 1M of random letters to test it
val = '' . join ( random . choice ( string . ascii_letters ) for i in range ( 1000000 ))
def test_pure_python ( benchmark ):
benchmark ( count_doubles , val )
def test_regex ( benchmark ):
benchmark ( count_doubles_regex , val )運行pytest進行比較:
$ pytest doubles.py
=============================================================================
platform linux -- Python 3.6.0, pytest-3.2.3, py-1.4.34, pluggy-0.4.
benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_roun
rootdir: /Projects/rustpy, inifile:
plugins: benchmark-3.1.1
collected 2 items
doubles.py ..
-----------------------------------------------------------------------------
Name (time in ms) Min Max Mean
-----------------------------------------------------------------------------
test_regex 24.6824 (1.0) 32.3960 (1.0) 27.0167 (1.0)
test_pure_python 51.4964 (2.09) 62.5680 (1.93) 52.8334 (1.96)
-----------------------------------------------------------------------------
讓我們Mean進行比較:
板條箱是我們稱之為生鏽包裹的方式。
安裝了生鏽(推薦的方法是https://www.rustup.rs/)Rust也可以在Fedora和Rhel Rust-toolset上使用
我使用了
rustc 1.21.0
在同一文件夾中運行:
cargo new pyext-myrustlib它在同一文件夾中創建一個新的Rust Project,稱為pyext-myrustlib包含Cargo.toml (貨物是Rust Package Manager)和src/lib.rs (我們在其中編寫我們的庫實現)
它將使用rust-cpython板條箱作為依賴性,並告訴貨物生成一個從Python進口的dylib
[ package ]
name = " pyext-myrustlib "
version = " 0.1.0 "
authors = [ " Bruno Rocha <[email protected]> " ]
[ lib ]
name = " myrustlib "
crate-type = [ " dylib " ]
[ dependencies . cpython ]
version = " 0.1 "
features = [ " extension-module " ]我們需要做什麼:
從cpython板條箱中導入所有宏
從cpython到我們的lib範圍,以Python和PyResult類型
在Rust中寫入count_doubles函數實現,請注意,這與純Python版本非常相似,除了:
Python為第一個參數,是對Python解釋器的引用,並允許Rust使用Python GIL&str打字val作為參考PyResult ,是一種允許提高Python例外的類型PyResult對象Ok(total) (結果是一種枚舉類型,代表成功(OK)或失敗(ERR)),並且由於我們的功能有望返回PyResult ,編譯器將負責將我們的Ok包裝在該類型上。 (請注意,我們的Pyresult期望u64作為回報值)使用py_module_initializer!宏我們將新屬性註冊到LIB,包括__doc__ ,還添加count_doubles屬性參考我們的Rust implementation of the function
try!宏等同於Python的try.. exceptOk(()) - ()是一個空的結果元組, None python # [ macro_use ]
extern crate cpython ;
use cpython :: { Python , PyResult } ;
fn count_doubles ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
for ( c1 , c2 ) in val . chars ( ) . zip ( val . chars ( ) . skip ( 1 ) ) {
if c1 == c2 {
total += 1 ;
}
}
Ok ( total )
}
py_module_initializer ! ( libmyrustlib , initlibmyrustlib , PyInit_myrustlib , | py , m | {
try ! ( m . add ( py , " __doc__ " , " This module is implemented in Rust " ) ) ;
try! ( m . add ( py , " count_doubles " , py_fn ! ( py , count_doubles ( val : & str ) ) ) ) ;
Ok ( ( ) )
} ) ;現在讓我們用貨物建造
$ cargo build --release
Finished release [optimized] target(s) in 0.0 secs
$ ls -la target/release/libmyrustlib *
target/release/libmyrustlib.d
target/release/libmyrustlib.so * < -- Our dylib is here現在,讓我們將生成的.so lib複製到我們doubles.py的同一文件夾。
注意:在Fedora上,您必須在其他系統中獲得
.so.dylib並且可以將其重命名為.so
$ cd ..
$ ls
doubles.py pyext-myrustlib/
$ cp pyext-myrustlib/target/release/libmyrustlib.so myrustlib.so
$ ls
doubles.py myrustlib.so pyext-myrustlib/將
myrustlib.so放在同一文件夾中或添加到python路徑中,可以直接導入,因為它是python模塊。
編輯您的doubles.py現在導入我們的Rust implemented版本,並為其添加benchmark 。
import re
import string
import random
import myrustlib # <-- Import the Rust implemented module (myrustlib.so)
def count_doubles ( val ):
"""Count repeated pair of chars ins a string"""
total = 0
for c1 , c2 in zip ( val , val [ 1 :]):
if c1 == c2 :
total += 1
return total
double_re = re . compile ( r'(?=(.)1)' )
def count_doubles_regex ( val ):
return len ( double_re . findall ( val ))
val = '' . join ( random . choice ( string . ascii_letters ) for i in range ( 1000000 ))
def test_pure_python ( benchmark ):
benchmark ( count_doubles , val )
def test_regex ( benchmark ):
benchmark ( count_doubles_regex , val )
def test_rust ( benchmark ): # <-- Benchmark the Rust version
benchmark ( myrustlib . count_doubles , val )$ pytest doubles.py
==============================================================================
platform linux -- Python 3.6.0, pytest-3.2.3, py-1.4.34, pluggy-0.4.
benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_round
rootdir: /Projects/rustpy, inifile:
plugins: benchmark-3.1.1
collected 3 items
doubles_rust.py ...
-----------------------------------------------------------------------------
Name (time in ms) Min Max Mean
-----------------------------------------------------------------------------
test_rust 2.5555 (1.0) 2.9296 (1.0) 2.6085 (1.0)
test_regex 25.6049 (10.02) 27.2190 (9.29) 25.8876 (9.92)
test_pure_python 52.9428 (20.72) 56.3666 (19.24) 53.9732 (20.69)
-----------------------------------------------------------------------------讓我們Mean進行比較:
生鏽實施的速度比Python Regex快10倍,比Pure Python版本快21倍。
有趣的是, Regex版本僅比Pure Python快2倍:)
注意:對於這種特定情況,這個數字僅在其他情況下可能有所不同。
本文發表後,我對R/Python以及R/Rust收到了一些評論
貢獻作為拉請求,如果您認為可以改善功能,則可以發送新的。
感謝:喬什·斯通(Josh Stone),我們獲得了更好的Rust實現,它僅迭代了一次弦樂,而且還要等效於Python。
感謝:Purple Pixie,我們使用itertools獲得了Python實現,但是此版本的執行情況並沒有更好,需要改進。
fn count_doubles_once ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
let mut chars = val . chars ( ) ;
if let Some ( mut c1 ) = chars . next ( ) {
for c2 in chars {
if c1 == c2 {
total += 1 ;
}
c1 = c2 ;
}
}
Ok ( total )
} def count_doubles_once ( val ):
total = 0
chars = iter ( val )
c1 = next ( chars )
for c2 in chars :
if c1 == c2 :
total += 1
c1 = c2
return total import itertools
def count_doubles_itertools ( val ):
c1s , c2s = itertools . tee ( val )
next ( c2s , None )
total = 0
for c1 , c2 in zip ( c1s , c2s ):
if c1 == c2 :
total += 1
return total好的,這不是這篇文章的目的,這篇文章從來都不是要比較Rust X other language ,這篇文章專門講述了如何使用Rust擴展和加快Python的速度,並且這樣做意味著您有充分的理由選擇Rust,而不是other language ,而不是通過其生態系統,其安全性和工具,或者只是跟隨炒作,或者僅僅是因為您像Rust一樣,因為您不願意使用該帖子,該帖子與此處有關。
我(個人)可能會說,Rust是新的future proof ,因為它是新的,並且由於其生態系統,工具和社區而有很多改進,而且因為我對Rust語法感到滿意,所以我真的很喜歡!
因此,正如預期的那樣,人們開始抱怨使用其他語言,這成為一種基準,我認為這很酷!
因此,作為我改進的一部分,有些人在Hacker News上也發送了想法,Martinxyz使用C和SWIG進行了實施,表現非常出色。
C代碼(SWIG樣板列入)
uint64_t count_byte_doubles ( char * str ) {
uint64_t count = 0 ;
while ( str [ 0 ] && str [ 1 ]) {
if ( str [ 0 ] == str [ 1 ]) count ++ ;
str ++ ;
}
return count ;
}我們的紅色帽帽喬什·斯通(Josh Stone)通過用bytes代替chars ,這再次改善了生鏽的實現,因此與C進行了公平的競爭,因為C正在比較字節而不是Unicode Chars。
fn count_doubles_once_bytes ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
let mut chars = val . bytes ( ) ;
if let Some ( mut c1 ) = chars . next ( ) {
for c2 in chars {
if c1 == c2 {
total += 1 ;
}
c1 = c2 ;
}
}
Ok ( total )
}還有一些想法可以比較Python list comprehension和numpy ,所以我在這裡包括
numpy:
import numpy as np
def count_double_numpy ( val ):
ng = np . fromstring ( val , dtype = np . byte )
return np . sum ( ng [: - 1 ] == ng [ 1 :])列表理解
def count_doubles_comprehension ( val ):
return sum ( 1 for c1 , c2 in zip ( val , val [ 1 :]) if c1 == c2 )完整的測試用例在repository test_all.py文件上。
-------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
-------------------------------------------------------------------------------------------------
test_rust_bytes_once 476.7920 (1.0) 830.5610 (1.0) 486.6116 (1.0)
test_c_swig_bytes_once 795.3460 (1.67) 1,504.3380 (1.81) 827.3898 (1.70)
test_rust_once 985.9520 (2.07) 1,483.8120 (1.79) 1,017.4251 (2.09)
test_numpy 1,001.3880 (2.10) 2,461.1200 (2.96) 1,274.8132 (2.62)
test_rust 2,555.0810 (5.36) 3,066.0430 (3.69) 2,609.7403 (5.36)
test_regex 24,787.0670 (51.99) 26,513.1520 (31.92) 25,333.8143 (52.06)
test_pure_python_once 36,447.0790 (76.44) 48,596.5340 (58.51) 38,074.5863 (78.24)
test_python_comprehension 49,166.0560 (103.12) 50,832.1220 (61.20) 49,699.2122 (102.13)
test_pure_python 49,586.3750 (104.00) 50,697.3780 (61.04) 50,148.6596 (103.06)
test_itertools 56,762.8920 (119.05) 69,660.0200 (83.87) 58,402.9442 (120.02)
-------------------------------------------------------------------------------------------------
new Rust implementation comparing bytes比舊比較Unicode chars好2倍Rust版本仍然比使用C更好unicode chars Rust仍然比numpy好Numpy比第一個在Unicode Chars上遇到雙重迭代問題的first Rust implementation要好list comprehension與使用pure Python沒有意義的差異注意:如果您想提出更改或改進,請在此處發送PR:https://github.com/rochacbruno/rust-python-example/
我收到了更多的貢獻,因為拉的請求之一是傑森·奈特(Jason Knight),以改善Rust
RUSTFLAGS= " -C target-cpu=native " cargo build --release對於那些對與numba進行比較的人,Shyba實施了它,並且可以在Numba Branch https://github.com/rochacbruno/rust-python-example/tree/tree/numba中使用。
from numba import jit
@ jit ( nopython = True , cache = True )
def count_doubles_once_numba ( val ):
total = 0
chars = iter ( val )
c1 = next ( chars )
for c2 in chars :
if c1 == c2 :
total += 1
c1 = c2
return total用Numba在頂部查看新結果,非常接近Rust
----------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
----------------------------------------------------------------------------------------------------
test_pure_python_once_numba 292.0990 (1.0) 317.7590 (1.0) 296.7477 (1.0)
test_numpy_numba 326.2470 (1.12) 526.1350 (1.66) 338.1704 (1.14)
test_rust_bytes_once 336.0620 (1.15) 1,053.0090 (3.31) 342.5122 (1.15)
test_c_swig_bytes_once 375.6310 (1.29) 1,389.9070 (4.37) 388.9181 (1.31)
test_rust_once 986.0360 (3.38) 2,498.5850 (7.86) 1,006.5819 (3.39)
test_numpy 1,137.1750 (3.89) 2,000.5430 (6.30) 1,167.2551 (3.93)
test_rust 2,555.1400 (8.75) 3,645.3900 (11.47) 2,592.0419 (8.73)
test_regex 22,597.1750 (77.36) 25,027.2820 (78.76) 22,851.8456 (77.01)
test_pure_python_once 32,418.8830 (110.99) 34,818.0800 (109.57) 32,756.3244 (110.38)
test_pure_python 43,823.5140 (150.03) 45,961.8460 (144.64) 44,367.1028 (149.51)
test_python_comprehension 46,360.1640 (158.71) 50,578.1740 (159.17) 46,986.8058 (158.34)
test_itertools 49,080.8640 (168.03) 51,016.5230 (160.55) 49,405.2562 (166.49)
----------------------------------------------------------------------------------------------------邁克·弗萊徹(Mike Fletcher)在cython中也有Cython實施
結果:
----------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
----------------------------------------------------------------------------------------------------
test_rust_bytes_once 336.7590 (1.0) 806.2610 (1.0) 346.5317 (1.0)
test_cython 756.1610 (2.25) 2,343.3680 (2.91) 785.6455 (2.27)
test_c_swig_bytes_once 802.4250 (2.38) 1,632.4290 (2.02) 840.8603 (2.43)
----------------------------------------------------------------------------------------------------回到這篇文章的目的,如何用Rust加速您的蟒蛇:
在此示例中, Rust的執行速度比我們的Pure Python快100倍。
Rust不會神奇地拯救您,您必須知道該語言才能實現聰明的解決方案,並且一旦在右側實施,就可以在績效方面的價值與C一樣多,並且還具有驚人的工具,生態系統,社區和安全獎金。
Rust可能還不是其複雜性水平的general purpose language ,並且可能還不是編寫常見的簡單applications (例如web和test automation腳本)的更好選擇。
但是,對於已知Python是瓶頸和您的自然選擇的specific parts ,將實施C/C++擴展,在Rust中編寫此擴展名似乎很容易維護。
生鏽還有很多改進,還有許多其他板條箱可以提供Python <--> Rust Entegration。即使您現在還不包括工具帶中的語言,也要睜開眼睛對未來也是值得的!
該出版物上的示例的靈感來自加拿大Pycon的Samuel Cormier-iijima的Extending Python with Rust 。視頻在這裡:https://www.youtube.com/watch?v=-ylbuezkg4m
而且, My Python is a little Rust-y丹·卡拉漢(Dan Callahan)在蒙特利爾(Pycon Montreal)的丹·卡拉漢(Dan Callahan)。視頻在這裡:https://www.youtube.com/watch?v=3cwj0mh-4ma
其他參考文獻:
加入社區:
加入Rust社區,您可以在https://www.rust-lang.org/en-us/community.html中找到組鏈接
如果您會說葡萄牙語,我建議您加入https://t.me/rustlangbr,並且YouTube上還有http://bit.ly/canalrustbr。
布魯諾·羅沙(Bruno Rocha)
更多信息:http://about.me/rochacbruno和http://brunorocha.org