محک زدن Rust برای سرور بدون سرور – انجمن DEV

بیایید با دو شروع کنیم خیلی سوالات مهم:
(1) چرا باید به آن اهمیت بدهم معیار زنگ می زند زیرا در حال حاضر فوق العاده سریع است؟
این سوالی است که در این پست سعی خواهیم کرد به آن پاسخ دهیم!
(2) آیا نسخه نمایشی 🍕 وجود خواهد داشت؟
البته خودت میدونی که من یک عاشق واقعی 🍕🍕🍕 هستم!
بدون سرور چه ویژگی خاصی دارد؟
بنچمارک مخصوص بدون سرور نیست. اما در اجزای بدون سرور، مانند توابع AWS Lambda، عملکرد واقعاً به دو دلیل مهم است:
-
مدت زمان شروع سرد (خبر خوب، همانطور که در معیار به روز رسانی روزانه من می بینید، Rust واقعاً کارآمد است)
-
مدت زمان اجرا، زیرا AWS در حال صدور صورت حساب است در هر میلی ثانیه.
بیایید ببینیم چگونه می توانیم این مدت زمان اجرا را با استفاده از معیارها اندازه گیری و بهبود دهیم!
قرار است چه چیزی را معیار قرار دهیم؟
بیایید یک مثال بسیار ساده بزنیم:
a function which returns the pizza of the day.
🍕 (بهت گفتم)
ما دو پیاده سازی متفاوت را با هم مقایسه خواهیم کرد:
- یکی با HashMap –
std::collections::HashMap
- یکی با Vec –
std::vec::Vec
(اگر چیز زیادی در مورد Rust نمی دانید، ممکن است بخواهید قبل از شروع به برخی از کدهای اصلی Rust نگاهی بیندازید، مانند کتاب Rust یا کانال Rust در یوتیوب من.)
ابتدا بیایید خودمان را تعریف کنیم PizzaStore
صفت:
pub trait PizzaStore {
fn get_pizza_of_the_day(&self, day_index: i32) -> &str;
}
و اولین پیاده سازی ما با HashMap:
// let's make sure we don't initialize a new HashMap each time
pub struct PizzaHashMap<'a> {
cache: HashMap<i32, &'a str>,
}
impl<'a> PizzaHashMap<'a> {
pub fn new() -> Self {
PizzaHashMap {
cache: HashMap::from([
(0, "margherita"),
(1, "deluxe"),
(2, "veggie"),
(3, "mushrooms"),
(4, "bacon"),
(5, "four cheese"),
(6, "pepperoni"),
// what's your favorite?
]),
}
}
}
impl<'a> PizzaStore for PizzaHashMap<'a> {
// let's get the pizza of the day from the cache (HashMap)
fn get_pizza_of_the_day(&self, day_index: i32) -> &str {
match self.cache.get(&day_index) {
Some(&pizza) => pizza,
None => panic!("could not find the pizza"),
}
}
}
نوشتن اولین معیار ما
جعبه هایی برای ایجاد معیار وجود دارد اما ما از آنها استفاده خواهیم کرد criterion
اینجا.
بیایید با ایجاد یک شروع کنیم benches
پوشه حاوی الف benchmark.rs
فایل کنید و اولین معیار خود را اضافه کنید:
fn criterion_hashmap(c: &mut Criterion) {
let mut rng = rand::thread_rng();
// we create the cache outside of the bench function so only one HashMap will be created
let pizza_store_hashmap = PizzaHashMap::new();
// we call get_pizza_of_the_day with a random day index
c.bench_function("with hashmap", |b| b.iter(|| pizza_store_hashmap.get_pizza_of_the_day(rng.gen_range(0..7))));
}
ما همچنین به یک نیمکت نیاز داریم (زیرا بعداً معیارهای بیشتری اضافه خواهیم کرد)
criterion_group!(benches, criterion_hashmap);
criterion_main!(benches);
خودشه! بیایید آن را اجرا کنیم cargo bench
به طور پیش فرض، عملکرد ما را حدود 5 ثانیه اجرا می کند (یعنی حدود 300 میلیون تکرار)
Running benches/benchmark.rs (target/release/deps/benchmark-2f28819806c8c7c9)
with hashmap time: [13.069 ns 13.090 ns 13.114 ns]
مقادیر چپ و راست کران های پایین و بالایی هستند.
عدد وسط بهترین تخمین در مورد مدت زمان احتمالی هر تکرار است.
توجه داشته باشید که این 3 عدد بسیار شبیه هم هستند. اگر به عنوان مثال به شبکه وابسته باشید، این مورد صادق نخواهد بود.
اجرای دوم: با Vec
بیایید یک پیاده سازی متفاوت با استفاده از Vec به جای استفاده از HashMap با استفاده از کد زیر ایجاد کنیم.
pub struct PizzaVec<'a> {
cache: Vec<&'a str>
}
impl<'a> PizzaVec<'a> {
pub fn new() -> Self {
PizzaVec {
cache: vec!["margherita","deluxe","veggie","mushrooms","bacon","four cheese","pepperoni"],
}
}
}
impl<'a> PizzaStore for PizzaVec<'a> {
fn get_pizza_of_the_day(&self, day_index: i32) -> &str {
match self.cache.get(day_index as usize) {
Some(&pizza) => pizza,
None => panic!("could not find the pizza"),
}
}
}
بیایید یک معیار جدید ایجاد کنیم تا بتوانیم مقایسه کنیم (هدف معیارها این است!)
برگشت داخل benchmark.rs
می توانیم اضافه کنیم
fn criterion_vec(c: &mut Criterion) {
let mut rng = rand::thread_rng();
let pizza_store_vec = PizzaVec::new();
c.bench_function("with vector", |b| b.iter(|| pizza_store_vec.get_pizza_of_the_day(rng.gen_range(0..7))));
}
و گروه ما را برای گنجاندن این معیار جدید به روز کنید:
criterion_group!(benches, criterion_hashmap, criterion_vec);
بنابراین ما می توانیم معیارهای خود را مجدداً با موارد زیر اجرا کنیم: cargo bench
و نتیجه را بررسی کنید!
with hashmap time: [13.096 ns 13.117 ns 13.141 ns]
with vector time: [7.5832 ns 7.5958 ns 7.6097 ns]
با جایگزینی HashMap با Vec، برنامه ما اکنون تقریباً در حال اجرا است دو برابر سریعتر!
این یک یادآوری عالی است که چگونه انتخاب ساختار داده بسته به مورد استفاده واقعاً مهم است
بسیار خوب، اما چگونه به سرور بدون سرور ترجمه می شود؟
دو تابع AWS Lambda با تعبیه هر یک از پیاده سازی ها مستقر شده اند.
هر تابع لامبدا تماس می گیرد 10_000_000 بار get_pizza_of_the_day
که در us-east-1
با 128MB
، در اینجا نتایج آمده است:
پیاده سازی | مدت زمان اجرا |
---|---|
HashMap | 267.92 میلیثانیه |
Vec | 87.98 میلیثانیه 🤯🤯 |
خودشه!
البته این یک مثال ساده بود اما امیدوارم شما را متقاعد کرده باشم که از معیارها برای بهینه سازی کد خود استفاده کنید!
سوال پاداش: این سربار از کجا می آید؟
منتظر پست بعدی وبلاگ در مورد باشید پروفیل زنگ زدگی!
❤️ ❤️ ❤️ این مطالب را دوست داشتید؟ ❤️ ❤️ ❤️