Rustは、ぼんやりと速く実行され、SegFaultsを防ぎ、スレッドの安全性を保証するシステムプログラミング言語です。
フィーチャー
撮影:rustlang.orgから
エリアスから聞いた錆のより良い説明メンバーとラストブラジル電報グループの錆の第一人者
Rustは、高レベルの抽象化を構築できる言語ですが、低レベルのコントロールを放棄することなく、つまり、データがメモリで表される方法の制御、使用するスレッドモデルなどを制御します。
Rustは、通常、コンピレーション中に最悪の並列性とメモリ管理エラーを検出できる言語です(同期せずに異なるスレッドのデータにアクセスしたり、扱われた後にデータを使用したりするなど)が、自分が何をしているのかを本当に知っている場合にハッチエスケープを与えます。
錆は、ランタイムがないため、ランタイムと統合するために使用できる言語です。プログラムnode.js、またはPythonプログラム、またはRuby、Luaなどのプログラムによって呼び出されるネイティブ拡張機能を記述できます。一方、これらの言語を使用してRustのプログラムをスクリプト化できます。 - 「エリアス・ガブリエル・アマラル・ダ・シルバ」

パイソンを錆びながら拡張するのに役立つ錆パッケージがたくさんあります。
Armin Ronacher(Flaskの作成者)が作成したMilksnakeとPyo3がPythonインタープリター用の錆びたバインディングに言及することができます
下部の完全な参照リストを参照してください。
この投稿では、Rust Cpythonを使用します。テストしたのは唯一のもので、安定したバージョンのRustと互換性があり、使用するのが簡単だとわかりました。
注:Pyo3はRust-Cpythonのフォークであり、多くの改善が伴いますが、NightlyバージョンのRustでのみ機能するため、この投稿にはstable舎を使用することを好みました。
長所: Pythonから錆機能やインポートを作成し、ベンチマークでわかるように、パフォーマンスの面で価値があることは非常に簡単です。
短所:プロジェクト/LIB/フレームワークの分布は、環境とアーキテクチャのバリエーションのためにターゲットシステムに錆びたモジュールをコンパイルすることを要求します。純粋なPythonライブラリをインストールするときにはないコンパイル段階があります。錆びた状態を使用したり、Pythonホイールのバイナリデータを具体化したりするためにMilksnakeを使用して簡単にすることができます。
はい、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をとりましょう。
クレートは、私たちがRustパッケージと呼ぶ方法です。
さびを取り付けた(推奨方法はhttps://www.rustup.rs/)錆びているrust rust-toolsetでも入手できます
rustc 1.21.0使用しました
同じフォルダーで実行されます:
cargo new pyext-myrustlib Cargo.tomlを含むpyext-myrustlibと呼ばれる同じフォルダーに新しいRustプロジェクトを作成します(貨物はRustパッケージマネージャー)と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 Crateからすべてのマクロをインポートします
Cpythonから私たちのlibスコープまで、 PythonとPyResultタイプを取ります
count_doubles関数の実装をRustます。これは、以下を除いて純粋なpythonバージョンに非常に似ていることに注意してください。
Python最初の引数として使用します。これは、Pythonインタープリターへの参照であり、RustがPython GIL使用できるようにします&strタイプvalを受信しますPyResult返しますOk(total)のPyResultオブジェクトを返します(結果は成功(OK)または障害(ERR)のいずれかを表す列挙タイプです)。関数がPyResultを返すと予想されるため、コンパイラはそのタイプでOkをラッピングします。 (私たちのPyresultはu64返品値として期待していることに注意してください) py_module_initializer!マクロ__doc__を含むlibに新しい属性を登録し、 Rust implementation of the functionを参照するcount_doubles属性を追加します
try! Pythonのtry.. exceptOk(()) - the ()は空の結果のタプルであり、pythonのNoneに相当するものです # [ 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をとりましょう。
Rustの実装は、Python Regexよりも10倍高速で、Pure Pythonバージョンよりも21倍高速になります。
Regexバージョンは純粋なPythonよりも2倍高速であることが興味深い:)
注:その数字は、この特定のシナリオでのみ理にかなっています。他のケースでは、比較が異なる場合があります。
この記事が公開された後、R/PythonとR/Rustについてもコメントを受け取りました
貢献はプルリクエストとしてもたらされ、機能を改善できると思われる場合は新しい送信できます。
ありがとう:Josh Stone私たちは錆のためのより良い実装を手に入れました。
おかげで: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 other language比較することではありませんでした。この投稿は、パイソンの拡張とスピードを拡張してスピードアップするために錆を使用する方法に関するものではありませんでした。
私は(個人的には、錆は新しいものであり、その生態系、工具、コミュニティのおかげで、またRust Syntaxに満足していると感じているため、今後多くの改善があるため、よりfuture proofの証拠であると言うかもしれません。
したがって、予想通り、人々は他の言語の使用について不平を言い始め、それは一種のベンチマークになり、私はそれがクールだと思います!
そのため、改善の要求の一環として、ハッカーニュースの一部の人々もアイデアを送信したため、Martinxyzは非常にうまく機能するCとSWIGを使用して実装を送信しました。
cコード(swig bulerplate emomped)
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 ;
}そして、私たちの仲間のレッドハッタージョシュストーンは、 charsをbytesに置き換えることでRustの実装を再度改善したため、CはUnicode Charの代わりにバイトを比較しているため、 Cとの公正な競争です。
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 )完全なテストケースは、リポジトリ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ユニコードcharsを比較する古い比較よりも2倍優れていますRustバージョンは、swigを使用してCよりも優れていますunicode charsを比較するRust 、 numpyよりも優れていますfirst Rust implementationよりもNumpy優れていますlist comprehensionを使用することは、 pure Pythonを使用するよりも大きな違いを生みません注:変更または改善を提案したい場合は、ここからPRを送信します:https://github.com/rochacbruno/rust-python-example/
プルリクエストの1つがジェイソンナイトによるRustを使用して改善するために、私はより多くの貢献を受けました
RUSTFLAGS= " -C target-cpu=native " cargo build --releaseそして、 numbaとの比較に興味がある人のために、Shybaがそれを実装し、Numba Branch https://github.com/rochacbruno/rust-python-example/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 totalnumbaが上部にある新しい結果を見て、錆にかなり近い
----------------------------------------------------------------------------------------------------
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)
----------------------------------------------------------------------------------------------------また、ブランチにマイクフレッチャーによる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)
----------------------------------------------------------------------------------------------------この投稿の目的に戻ると、Pythonをスピードアップする方法を錆びます。
この例では、 Rustは純粋なPythonよりも100倍速く実行されました。
Rust魔法のようにあなたを救うことはありません。巧妙なソリューションを実装できるように言語を知る必要があります。
Rust 、その複雑さのレベルによって選択されたgeneral purpose languageではない場合があり、 webサイトやtest automationスクリプトなどの一般的な簡単なapplicationsを作成するためのより良い選択ではない場合があります。
ただし、Pythonがボトルネックであることが知られており、自然な選択がC/C++拡張機能を実装することが知られているプロジェクトのspecific partsでは、この拡張機能を錆びて書くことは簡単で維持が良いようです。
Python <--> Rust Integrationを提供するために、錆びることや他の多くの木枠がまだ多くの改善があります。あなたが今あなたのツールベルトに言語を含めていなくても、未来に目を向けることは本当に価値があります!
この出版物の例は、カナダのピコンのサミュエル・コルミエ・イジマによるExtending Python with Rustことに触発されています。ここのビデオ:https://www.youtube.com/watch?v=-ylbuezkg4m
また、私のPythonによって、 Pycon MontrealのDan CallahanによるMy Python is a little Rust-y 。ここのビデオ:https://www.youtube.com/watch?v=3cwj0mh-4ma
その他の参照:
コミュニティに参加:
Rust Communityに参加してください。https://www.rustlang.org/en-us/community.htmlでグループリンクを見つけることができます
ポルトガル語を話す場合は、 https://t.me/rustlangbrに参加することをお勧めします。また、YouTubeにhttp://bit.ly/canalrustbrもあります。
ブルーノ・ロシャ
詳細:http://about.me/rochacbrunoおよびhttp://brunorocha.org