Criterion Rust: دليل شامل مع أمثلة عملية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما هو Criterion وما أهميته؟

Criterion.rs هي مكتبة قوية ومرنة لاختبار الأداء (البنشمارك) في لغة Rust. وهي مشابهة للمكتبة الشهيرة Criterion الخاصة بـ Haskell، لكنها مُكيَّفة خصيصًا لبيئة Rust.

لماذا تحتاج إلى Criterion؟ على عكس قياس الوقت البسيط باستخدام std::time::Instant، يوفر Criterion:

  • تحليل إحصائي: يقوم تلقائيًا بتشغيل الكود عدة مرات، ويجمع عينات من وقت التنفيذ، ويحسب المتوسط والوسيط والانحراف المعياري والنسب المئوية.
  • مقارنة مع مرجع: يتيح لك مقارنة أداء الإصدار الحالي من الكود مع الإصدار السابق (اختبار الانحدار).
  • إنشاء تقارير: يُنشئ تقارير HTML جميلة تحتوي على رسوم بيانية وجداول.
  • اختبار الأداء البارامتري: اختبر أداء الدالة بسهولة باستخدام بيانات إدخال مختلفة.
  • مقاومة التشويش: خوارزميات Criterion مقاومة لتقلبات وقت التنفيذ الناتجة عن عمليات الخلفية في نظام التشغيل.

هذه المكتبة لا غنى عنها للمطورين الذين يرغبون في:

  • تحسين أجزاء الكود الحرجة.
  • التأكد من أن الكود الجديد لا يُبطئ الوظائف القديمة.
  • اتخاذ قرارات مبنية على البيانات، وليس على التخمين.

تثبيت Criterion في مشروع Rust

على عكس Python (pip) أو Node.js (npm)، يتم تثبيت المكتبات في Rust عبر مدير الحزم Cargo. Criterion ليست مكتبة للكود الرئيسي، بل أداة للاختبار، لذا تُضاف في قسم [dev-dependencies] من ملف Cargo.toml.

ستحتاج أيضًا إلى إنشاء مجلد benches/ في جذر مشروعك (بجانب مجلد src/).

الخطوة 1: أضف التبعية في Cargo.toml

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }

ملاحظة: العلامة features = ["html_reports"] تُفعّل إنشاء تقارير HTML. بدونها، ستحصل على مخرجات نصية فقط في الطرفية.

الخطوة 2: أنشئ ملف البنشمارك

أنشئ ملفًا باسم benches/my_benchmark.rs (يمكنك اختيار أي اسم).

الخطوة 3: شغّل البنشمارك

cargo bench

هذا الأمر سيُجمّع مشروعك في وضع الإصدار (مع التحسينات) ويُشغّل جميع البنشماركات الموجودة في مجلد benches/.

بداية سريعة: أول بنشمارك لك

لنكتب بنشماركًا بسيطًا يقيس سرعة جمع الأرقام من 0 إلى 99.

// benches/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};

// الدالة التي سنختبرها
fn sum_slow(n: u64) -> u64 {
    let mut sum = 0;
    for i in 0..n {
        sum += i;
    }
    sum
}

fn sum_fast(n: u64) -> u64 {
    n * (n - 1) / 2
}

fn criterion_benchmark(c: &mut Criterion) {
    // تجميع البنشماركات
    let mut group = c.benchmark_group("sum_comparison");
    
    group.bench_function("slow", |b| b.iter(|| sum_slow(black_box(100))));
    group.bench_function("fast", |b| b.iter(|| sum_fast(black_box(100))));
    
    group.finish();
}

// الماكرو لتسجيل وتشغيل البنشماركات
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

ماذا يحدث هنا؟

  • black_box() — يُخبر المترجم بأن القيمة يجب أن تُمرّر "كما هي"، مما يمنع التحسين المبكر (قد يحسب المترجم الثابت في وقت التجميع، مما يُشوّه النتيجة).
  • b.iter() — الحلقة الرئيسية للبنشمارك. سيقوم Criterion باستدعاء هذا الإغلاق عدة مرات وقياس الوقت.
  • criterion_group! و criterion_main! — ماكرو لتسجيل وتشغيل البنشماركات.

توصيات