Rust هي لغة برمجة الأنظمة التي تعمل بسرعة كبيرة ، وتمنع segfaults ، وتضمن سلامة الخيوط.
يضم
مأخوذة من: من الصدأ lang.org
وصف أفضل للصدأ الذي سمعته من إلياس عضوًا وعلمًا للصدأ من مجموعة الصدأ البرازيلي برقية
Rust هي لغة تسمح لك ببناء تجريدات عالية المستوى ، ولكن دون التخلي عن التحكم في المستوى المنخفض - أي التحكم في كيفية تمثيل البيانات في الذاكرة ، والتحكم في نموذج الخيوط الذي تريد استخدامه وما إلى ذلك.
Rust هي لغة يمكنها عادةً اكتشافها ، أثناء التجميع ، أسوأ أخطاء التوازي وإدارة الذاكرة (مثل الوصول إلى البيانات على مؤشرات ترابط مختلفة دون مزامنة ، أو استخدام البيانات بعد تعاملها) ، ولكنها تمنحك هروبًا في الحالة التي تعرفها حقًا ما تفعله.
Rust هي لغة ، نظرًا لأنه لا يوجد لديه وقت تشغيل ، يمكن استخدامه للتكامل مع أي وقت تشغيل ؛ يمكنك كتابة امتداد أصلي في الصدأ يسمى بواسطة برنامج Node.js ، أو بواسطة برنامج Python ، أو بواسطة برنامج في Ruby و Lua وما إلى ذلك ، ومن ناحية أخرى ، يمكنك نص برنامج في الصدأ باستخدام هذه اللغات. - "إلياس غابرييل أمارال دا سيلفا"

هناك مجموعة من حزم الصدأ هناك لمساعدتك في توسيع Python مع الصدأ.
أستطيع أن أذكر Milksnake الذي تم إنشاؤه بواسطة Armin Ronacher (منشئ القارورة) وكذلك Pyo3 روابط الصدأ لمترجم بيثون
انظر قائمة مرجعية كاملة في الأسفل.
بالنسبة لهذا المنشور ، سأستخدم Rust Cpython ، إنه الوحيد الذي اختبرته ، وهو متوافق مع إصدار ثابت من Rust ووجد أنه من السهل استخدامه.
ملاحظة : Pyo3 هو شوكة من Rust-Cpython ، ويأتي مع العديد من التحسينات ، ولكنه يعمل فقط مع الإصدار الليلي من Rust ، لذلك فضلت استخدام المستقر لهذا المنشور ، على أي حال يجب أن تعمل الأمثلة هنا أيضًا مع Pyo3.
PROS: من السهل حقًا كتابة وظائف الصدأ والاستيراد من Python وكما سترى من خلال المعايير التي تستحقها من حيث الأداء.
سلبيات: سيطلب توزيع مشروعك/LIB/Framework أن يتم تجميع وحدة الصدأ على النظام المستهدف بسبب تباين البيئة والهندسة المعمارية ، وستكون هناك مرحلة تجميع لا تملكها عند تثبيت مكتبات Python النقية ، يمكنك جعلها أسهل باستخدام صدأ الصدأ أو استخدام Milksnake لتضمين البيانات الثنائية في بيثون ويلز.
نعم ، تشتهر بيثون بأنه "بطيء" في بعض الحالات والخبر السار هو أن هذا لا يهم حقًا اعتمادًا على أهداف مشروعك وأولوياتك. بالنسبة لمعظم المشاريع ، لن تكون هذه التفاصيل مهمة للغاية.
ومع ذلك ، قد تواجه الحالة النادرة التي تستغرق فيها وظيفة أو وحدة واحدة وقتًا طويلاً ويتم اكتشافها على أنها عنق الزجاجة لأداء المشروع الخاص بك ، غالبًا ما يحدث مع تحليل السلسلة ومعالجة الصور.
دعنا نقول أن لديك وظيفة 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
ثم اكتب برنامج Python جديد يسمى doubles.py
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/) الصدأ متاح أيضًا على Fedora و Rhel Rust-Toolset
لقد استخدمت
rustc 1.21.0
في نفس المجلد المدى:
cargo new pyext-myrustlib إنه ينشئ مشروعًا جديدًا لـ Rust في نفس المجلد يسمى pyext-myrustlib يحتوي على Cargo.toml (Cargo هي مدير حزمة الصدأ) وأيضًا src/lib.rs (حيث نكتب تطبيق المكتبات لدينا)
سوف يستخدم قفص rust-cpython كاعتماد ويخبر البضائع لإنشاء dylib ليتم استيراده من Python
[ 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
خذ أنواع Python و PyResult من Cpython إلى نطاق LIB الخاص بنا
اكتب تطبيق وظيفة count_doubles في Rust ، لاحظ أن هذا يشبه إلى حد كبير إصدار Python النقي باستثناء:
Python كوسيطة أولى ، وهي إشارة إلى مترجم بيثون وتسمح للصدأ باستخدام Python GIL&str المكتوبة val كمرجعPyResult وهو نوع يسمح برفع استثناءات PythonPyResult في Ok(total) ( النتيجة هي نوع التعداد الذي يمثل إما النجاح (موافق) أو PyResult (ERR)) وبما أنه من المتوقع أن تُرجع وظيفتنا Ok على هذا النوع. (لاحظ أن Pyresult لدينا يتوقع u64 كقيمة إرجاع) باستخدام py_module_initializer! الماكرو نقوم بتسجيل سمات جديدة على lib ، بما في ذلك __doc__ وأيضًا نضيف سمة count_doubles التي تشير إلى Rust implementation of the function
try! الماكرو الذي يعادل try.. exceptOk(()) - () هي نتيجة فارغة ، أي ما يعادل 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 ، يجب أن تحصل
.dylib.soفي نظام آخر.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 الخاص بك أن يتم استيراده مباشرة ، بشفافية لأنها كانت وحدة بيثون.
قم بتحرير doubles.py الخاص بك. yepy الآن استيراد نسختنا 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 للمقارنة:
يمكن أن يكون تطبيق الصدأ أسرع 10x من Python Regex و 21x أسرع من إصدار Python النقي.
من المثير للاهتمام أن إصدار Regex هو فقط 2x أسرع من Python النقي :)
ملاحظة: أن الأرقام منطقية فقط لهذا السيناريو بالذات ، قد تكون المقارنة مختلفة.
بعد نشر هذه المقالة ، تلقيت بعض التعليقات على R/Python وأيضًا على R/Rust
تأتي المساهمات كطلبات سحب ويمكنك إرسال جديد إذا كنت تعتقد أنه يمكن تحسين الوظائف.
بفضل: Josh Stone ، حصلنا على تنفيذ أفضل لـ Rust والذي يكرر السلسلة مرة واحدة فقط وأيضًا معادلة Python.
بفضل: Purple Pixie ، حصلنا على تطبيق Python باستخدام itertools ، ومع ذلك لا يحتاج هذا الإصدار إلى تحسينات أفضل.
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 حسنًا ، ليس هذا هو الغرض من هذا المنشور ، لم يكن هذا المنشور مطلقًا مقارنة other language Rust X الأخرى ، وكان هذا المنشور يتعلق بالتحديد كيفية استخدام Rust لتوسيع وتسريع Python وذلك ، فهذا يعني أن لديك سببًا جيدًا لاختيار Rust بدلاً من other language أو عن طريق نظامها الإيكولوجي أو عن طريق سلامتها وأدواتها أو لمجرد اتباعها ، أو ببساطة لأنك تحب Rust ، فهذا Post Post هنا مع إظهار كيفية استخدامها .
أنا (شخصياً) قد أقول إن الصدأ أكثر future proof لأنه جديد وهناك الكثير من التحسينات القادمة ، أيضًا بسبب نظامه البيئي والأدوات والمجتمع وأيضًا لأنني أشعر بالراحة مع بناء جملة الصدأ ، أحب ذلك حقًا!
لذلك ، كما هو متوقع ، بدأ الناس يشكون من استخدام لغات أخرى ويصبح نوعًا من المعيار ، وأعتقد أنه رائع!
لذا ، كجزء من طلبي للتحسين ، أرسل بعض الأشخاص على أخبار Hacker أيضًا أفكارًا ، حيث أرسل Martinxyz تطبيقًا باستخدام C و Swig الذي كان أداءً جيدًا للغاية.
C رمز (Swig Boilerplate Ommited)
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 ;
} وقد قام زملائنا Red Hatter Josh Stone بتحسين تطبيق الصدأ مرة أخرى من خلال استبدال chars bytes بحيث تكون منافسة عادلة مع C حيث تقارن C بايت بدلاً من chars Unicode.
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 )
} هناك أيضًا أفكار لمقارنة list comprehension Python و 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 ) حالة الاختبار الكاملة موجودة على ملف ropository 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 أفضل من 2x من المقارنة القديم التي تقارن unicode charsRust لا يزال أفضل من C باستخدام SwigRust مقارنة unicode chars لا يزال أفضل من numpyNumpy أفضل من first Rust implementation الذي كان لديه مشكلة التكرار المزدوج على unicode charslist 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 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 totalانظر إلى النتائج الجديدة مع Numba في الأعلى ، بالقرب من الصدأ
----------------------------------------------------------------------------------------------------
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 بواسطة Mike Fletcher في فرع cython https://github.com/rochacbruno/rust-python-example/tree/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 الخاص بك مع الصدأ بدأنا مع:
في هذا المثال ، أجرى الصدأ 100x أسرع من ثعباننا النقي .
لن ينقذك Rust بطريقة سحرية ، يجب أن تعرف اللغة لتتمكن من تنفيذ الحل الذكي وبمجرد تنفيذها في الصحيح ، فإنه يستحق ما يصل إلى C من حيث الأداء وتأتي أيضًا مع أدوات مذهلة ونظام بيئي ومكافآت المجتمع والسلامة.
قد لا تكون Rust بعد general purpose language المفضل من خلال مستوى التعقيد وقد لا تكون الخيار الأفضل حتى الآن لكتابة applications بسيطة شائعة مثل مواقع web ونصوص test automation .
ومع ذلك ، بالنسبة specific parts من المشروع حيث يُعرف أن بيثون هو عنق الزجاجة وسيقوم اختيارك الطبيعي بتنفيذ امتداد C/C++ ، فإن كتابة هذا التمديد في الصدأ يبدو سهلاً وأفضل للحفاظ عليه.
لا تزال هناك العديد من التحسينات التي تأتي في الصدأ والكثير من الصناديق الأخرى لتقديم تكامل Python <--> Rust . حتى لو لم تكن لديك اللغة في حزام الأدوات الخاص بك في الوقت الحالي ، فمن يستحق حقًا إبقاء العين مفتوحة للمستقبل!
الأمثلة على هذا المنشور مستوحاة من Extending Python with Rust الحديث من قبل صموئيل كورمييه Iijima في Pycon Canada . فيديو هنا: https://www.youtube.com/watch؟v=-ylbuezkg4m
وأيضًا من قبل My Python is a little Rust-y من دان كالاهان في بيكون مونتريال . الفيديو هنا: https://www.youtube.com/watch؟v=3CWJ0MH-4MA
مراجع أخرى:
انضم إلى المجتمع:
انضم إلى Community ، يمكنك العثور على روابط جماعية في https://www.rust-lang.org/en-us/community.html
إذا كنت تتحدث البرتغالية ، فإنك أوصيك بالانضمام إلى https://t.me/rustlangbr وهناك أيضًا http://bit.ly/canalrustbr على YouTube.
برونو روشا
مزيد من المعلومات: http://about.me/rochacbruno و http://brunorocha.org