ریاضی اعشاری سریعتر با عملگرهای جبری Rust
خلاصهٔ کاملتر
ایتامار ترنر-تراورینگ تو سایت PythonSpeed توضیح میده که چرا ریاضی اعشاری معمولاً کندتر از ریاضی صحیحه و Rust 1.98 چه راهحلی براش آورده. تو بنچمارک خودش، جمعزدن یه آرایهٔ یکمیلیونی از i64 حدود ۱۵۰ میکروثانیه طول میکشه و کامپایلر براش دستورهای SIMD ۲۵۶ بیتی تولید میکنه — یعنی چهار عدد در هر گام. همون کار روی f64 نزدیک به ۵۹۵ میکروثانیه میبره و هیچ دستور SIMD اعشاری تولید نمیشه.
ریشهٔ ماجرا یه قاعدهست: کامپایلر قول میده کد بهینهشده دقیقاً مثل کد اصلی رفتار کنه. برای عدد صحیح a + (b + c) با (a + b) + c برابره، پس دست کامپایلر بازه. ولی برای اعشار اینطور نیست؛ مثلاً 1e16 + 1.0 باز هم میشه 1e16. نویسنده نشون میده جمع یه آرایه که با 1e16 شروع میشه با همون آرایه بهصورت برعکس، جواب یکسانی نمیده — برای همین کامپایلر فرض میکنه این ترتیب عمدی بوده و دست بهش نمیزنه.
راهحل Rust 1.98 عملگرهای «جبری» مثل algebraic_add، algebraic_sub و algebraic_mul هستن. اینها به کامپایلر اجازه میدن از خواص جبری اعداد حقیقی استفاده کنه و ترتیب عملیات رو عوض کنه — ولی نقطهبهنقطه، نه بهصورت سراسری. یعنی میتونی تو یه بخش از الگوریتم دقت رو نگه داری و تو بخش دیگه سرعت بگیری. (تاریخ انتشار Rust 1.98 بیستم آگوست ۲۰۲۶ اعلام شده و نویسنده کدها رو با کانال beta اجرا کرده.)
مثال اصلی مقاله پیادهسازی pairwise summation ـه، همون الگوریتمی که NumPy برای کمکردن خطای انباشته استفاده میکنه: آرایه رو دو نصف کن، هر نصف رو بازگشتی جمع بزن و دو تا نتیجه رو با جمع معمولی و دقیق کنار هم بذار. زیر آستانهٔ ۱۲۸ عنصر، ترتیب دیگه مهم نیست و همونجا میشه از جمع جبری استفاده کرد:
let mut total: f64 = 0.0;
for value in values {
total = total.algebraic_add(*value);
}
totalنتیجهش هم جالبه: این نسخهٔ Rust با حدود ۱۴۵ میکروثانیه از np.sum() با ۱۹۱ میکروثانیه جلو میزنه و خطاش هم در همون حد NumPy میمونه (خطای ۶ واحد در برابر ۱۴ واحد، در حالی که جمع سادهٔ ترتیبی خطای یکمیلیونی داره). تو مثال دوم، محاسبهٔ مجموع مربع اختلاف دو آرایه با عملگرهای جبری از ۶۲۹ به ۳۷۱ میکروثانیه میرسه — تقریباً دو برابر سریعتر.
حرف آخر نویسنده اینه که هر دو نوع عملگر لازمن: اگه فقط از حالت سختگیرانه استفاده کنی کد کند میمونه، و اگه همهجا رو آزاد بذاری ممکنه کامپایلر کل الگوریتم دقتمحور رو بهینه کنه و از بین ببره. ترکیب هوشمندانهٔ این دو تاست که هم سرعت میده هم دقت.
نکات کلیدی:
- کامپایلر ریاضی اعشاری رو محافظهکارانه بهینه میکنه چون جمع اعشاری شرکتپذیر نیست
- Rust 1.98 عملگرهای جبری (algebraic_add، algebraic_sub، algebraic_mul) رو اضافه کرده
- کنترل نقطهبهنقطهست: تو بخشهای حساس دقت رو نگه دار، تو بخشهای آزاد سرعت بگیر
- pairwise summation نوشتهشده با این عملگرها از np.sum() سریعتره و دقتش هم مشابهه
- مجموع مربع اختلافها با عملگرهای جبری حدود دو برابر سریعتر شد
- بنچمارکها با target-cpu=x86-64-v3 گرفته شدن تا دستورهای SIMD مدرن فعال باشن




