آیا زبان سلام واقعاً از C و Rust سریع‌تر است؟ خوانشی دقیق از نتایج سنجش سرعت

مروری روش‌محور بر سنجش منتشرشده‌ی سلام: چه چیزی و چگونه اندازه‌گیری شد، میانگین‌ها چه چیزی را پنهان می‌کنند، سلام کجا می‌بازد و چرا خلاصه‌ی صادقانه «در رده‌ی C و Rust» است.

در این مقاله
  1. پاسخ کوتاه: سرعت زبان سلام در یک جدول
  2. این بنچمارک دقیقاً چه چیزی را می‌سنجد؟
  3. هر زمان چطور اندازه گرفته می‌شود؟
  4. نتیجه‌ها، با دقت خوانده‌شده
  5. جلوتر، برابر و عقب‌تر
  6. چرا برنامه‌های کوتاه گمراه‌کننده‌اند؟
  7. یک برنامه‌ی سنجش، کنار هم
  8. سنجش مسیریاب HTTP: سرعت وب‌سرور سلام
  9. چطور این سنجش را خودتان تکرار کنید؟
  10. پرسش‌های پرتکرار

نه؛ سنجش سرعت (Benchmark) زبان برنامه‌نویسی سلام نشان نمی‌دهد که سلام به معنای کلی از C یا Rust سریع‌تر است. آنچه نشان می‌دهد این است که روی ۳۹ برنامه‌ی کوچک که در همه‌ی زبان‌ها الگوریتم یکسان دارند، ساخت بهینه‌ی سلام در همان رده‌ی C و Rust تمام شد: میانگین ۱۰٫۶ میلی‌ثانیه برای سلام، ۱۱٫۷ میلی‌ثانیه برای C و ۱۱٫۳ میلی‌ثانیه برای Rust. در برابر سریع‌ترینِ C، C++، Rust و Go در هر برنامه، سلام در ۶ برنامه جلوتر، در ۲۷ برنامه برابر و در ۶ برنامه عقب‌تر بود. این فاصله‌ها کوچک‌اند، برنامه‌ها کوتاه‌اند و بخشی از هر عدد، زمان راه‌اندازی فرایند است نه کیفیت کد تولیدشده.

با این همه، برای یک زبان جوان نتیجه‌ی معناداری است: سلام را کنار زبان‌های کامپایلی و دور از زبان‌های تفسیری می‌نشاند، همان چیزی که از کامپایل بومی بدون ماشین مجازی و زباله‌روب انتظار می‌رود. این نوشته توضیح می‌دهد عددها چطور به دست آمدند، کجا گمراه‌کننده‌اند و چطور خودتان بررسی‌شان کنید.

پاسخ کوتاه: سرعت زبان سلام در یک جدول

ادعاآیا سنجش از آن پشتیبانی می‌کند؟
سلام به کدی کامپایل می‌شود که در رده‌ی سرعت C، C++، Rust و Go استبله، روی این مجموعه
سلام بسیار سریع‌تر از پایتون و PHP استبله: روی این مجموعه حدود ۵۶ برابر سریع‌تر از پایتون و ۹٫۶ برابر سریع‌تر از PHP
سلام از C یا Rust سریع‌تر استنه. فاصله‌ی میانگین در حدی است که زمان راه‌اندازی و نوسان میان اجراها می‌تواند توضیحش دهد
سلام برای برنامه‌های بزرگ، کارهای سنگین روی حافظه یا ساخت‌های طولانی سریع استسنجیده نشده

اگر قرار است فقط یک جمله در ذهنتان بماند، همان جمله‌ای باشد که صفحه‌ی سرعت خودش به کار می‌برد: میان زبان‌های کامپایلی، نتیجه‌ها را «یک رده» بخوانید، نه رتبه‌بندی.

این بنچمارک دقیقاً چه چیزی را می‌سنجد؟

مجموعه‌ی سنجش در پوشه‌ی benchmark/ مخزن است. ۴۲ برنامه دارد که هر کدام به C، C++، Rust، Go، پایتون، PHP و سلام نوشته شده است. قاعده‌ها ساده‌اند و ارزش گفتن دارند:

  • الگوریتم یکسان در همه‌جا. هر زبان همان گام‌ها را پیاده می‌کند، به همان شیوه‌ای که آن زبان معمولاً نوشته می‌شود. مجموعه اجازه نمی‌دهد زبانی الگوریتم زیرکانه‌تری به کار ببرد.
  • خروجی یکسان در همه‌جا. هر برنامه نتیجه‌ای قطعی چاپ می‌کند و خروجی همه‌ی نسخه‌ها پیش از هر زمان‌گیری با خروجی C مقایسه می‌شود. پاسخ سریعِ غلط حساب نمی‌شود.
  • بیشتر برنامه‌های کوچک و پردازنده‌محور. فیبوناچی بازگشتی و تکراری، شمارش عددهای اول، مرتب‌سازی، برنامه‌ریزی پویا، کار با رشته، ضرب ماتریس، بازی زندگی، انتگرال‌گیری عددی، و یک برنامه که یک خط را یک میلیون بار چاپ می‌کند.
  • سه برنامه‌ی کتابخانه‌ای. برنامه‌های ۴۰ تا ۴۲ از بسته‌های matrix و tensor خود سلام، از NumPy در پایتون و از حلقه‌های دست‌نویس در زبان‌های دیگر استفاده می‌کنند. این‌ها کتابخانه‌ها را مقایسه می‌کنند نه کامپایلرها را، و در میانگین‌های اصلی نیامده‌اند.

اجرایی که عددهای منتشرشده از آن آمده، در ۵ آبان ۱۴۰۵ (۲۷ اکتبر ۲۰۲۶) روی کامیت 8cfe206 انجام شد، روی یک اجراکننده‌ی ubuntu-24.04 در GitHub Actions (یک ماشین مجازی ابری اشتراکی با پردازنده‌ی AMD EPYC 9V45) و درون یک ایمیج Docker با Alpine Linux. ابزارها این‌ها بودند: gcc 15.2.0، rustc 1.96.1، Go 1.26.10، Python 3.14.10، PHP 8.5.12 و یک نسخه‌ی در حال توسعه از سلام ۰٫۶٫۰. C و C++ با gcc -O3، Rust با -C opt-level=3 و سلام با پشتیبان LLVM خود و -O3 ساخته شدند.

هر زمان چطور اندازه گرفته می‌شود؟

هر برنامه یک بار بدون زمان‌گیری برای گرم شدن اجرا می‌شود، سپس ۱۰ بار با ساعت دیواری، و سریع‌ترینِ این ۱۰ اجرا نگه داشته می‌شود. میانگین هر زبان، میانگین هندسی همین سریع‌ترین زمان‌هاست.

هر دو انتخاب قابل دفاع‌اند و هر دو پیامدهایی دارند که باید بدانید:

  • کمینه مزاحمت کارهای دیگرِ یک دستگاه اشتراکی را فیلتر می‌کند، چون چنین مزاحمتی فقط اجرا را کندتر می‌کند. به این پرسش پاسخ می‌دهد که «این برنامه این‌جا حداکثر چقدر سریع می‌تواند باشد»، نه این‌که «معمولاً چقدر سریع است». میانگین، میانه و پراکندگی هر برنامه در داده‌های خام هست، پس می‌توانید بررسی کنید که نتیجه‌گیری برای آن‌ها هم درست می‌ماند یا نه.
  • میانگین هندسی نمی‌گذارد یک برنامه‌ی کند بر همه چیز چیره شود. برنامه‌ی ۲۸ بسته به زبان ۷۰۰ تا ۸۴۰ میلی‌ثانیه و برنامه‌ی ۲۶ حدود ۲ میلی‌ثانیه طول می‌کشد؛ میانگین حسابی عملاً اندازه‌گیری برنامه‌ی ۲۸ می‌شد. با میانگین هندسی، بهبود ۱۰ درصدی در هر برنامه به یک اندازه میانگین را جابه‌جا می‌کند. روی دیگر سکه این است که میانگین دیگر چیزی درباره‌ی زمان کل نمی‌گوید.

زمان ساعت دیواری یک فرایند کامل، راه‌اندازی آن را هم شامل می‌شود: بار کردن فایل اجرایی، پیونددهنده‌ی پویا اگر باشد، راه‌اندازی محیط اجرای C، و در Go، محیط اجرای خود Go. برای برنامه‌ای که چند ثانیه اجرا می‌شود این نوسانی ناچیز است. برای برنامه‌ای که ۳ میلی‌ثانیه طول می‌کشد، بخش بزرگی از نتیجه است.

نتیجه‌ها، با دقت خوانده‌شده

این میانگین‌های هندسی روی ۳۹ برنامه‌ی هم‌الگوریتم است، همان‌طور که در صفحه‌ی سرعت منتشر شده:

زبانمیانگین هندسی
سلام۱۰٫۶۳ میلی‌ثانیه
Rust۱۱٫۲۹ میلی‌ثانیه
C۱۱٫۶۸ میلی‌ثانیه
C++۱۳٫۰۶ میلی‌ثانیه
Go۱۴٫۲۳ میلی‌ثانیه
PHP۱۰۲ میلی‌ثانیه
پایتون۵۹۸ میلی‌ثانیه

با هر ۴۲ برنامه هم ترتیب عوض نمی‌شود: سلام ۱۰٫۹۳ میلی‌ثانیه، Rust ۱۲٫۰۰، C ۱۲٫۱۰، C++ ۱۳٫۴۷ و Go ۱۵٫۰۷.

فاصله‌ی میان زبان‌های کامپایلی چند دهم میلی‌ثانیه است، روی برنامه‌هایی که حدود ۱۰ میلی‌ثانیه طول می‌کشند. فاصله تا زبان‌های تفسیری یک یا دو مرتبه‌ی بزرگی است. این دو، دو نوع نتیجه‌ی متفاوت‌اند و اولی را نباید رتبه‌بندی خواند.

دو مشاهده از همان اجرا این را ملموس می‌کند:

  1. ساختی از سلام که اصلاً بهینه‌ساز نداشت، میانگین ۱۱٫۰ میلی‌ثانیه گرفت؛ ساختی که با tcc کامپایل شد، کامپایلر C کوچکی که تقریباً هیچ بهینه‌سازی‌ای نمی‌کند. این تقریباً برابر با C کاملاً بهینه‌شده با gcc است. اگر ساختی بدون بهینه‌سازی هم‌پای -O3 بایستد، بیشتر ردیف‌های میانگین بهینه‌ساز را نمی‌سنجند.
  2. همان اجرا سلام را از راه پشتیبان C و کامپایل با gcc هم سنجید، و آن ساخت با C برابر درآمد. این همان چیزی است که انتظارش را دارید: وقتی دو برنامه از یک بهینه‌ساز می‌گذرند، کد ماشین مشابهی تولید می‌کنند.

با این نگاه جدول تک‌تک برنامه‌ها را در صفحه‌ی سرعت مرور کنید تا الگویی پیدا شود: در چند ردیف، زبان‌هایی که با LLVM ساخته می‌شوند با هم جابه‌جا می‌شوند. در 20_monte_carlo_pi هم Rust و هم سلام ۱۲٫۴ میلی‌ثانیه طول کشیدند و C ۲۲٫۹؛ در 28_prime_factorization زمان Rust ۷۰۱٫۴ میلی‌ثانیه، سلام ۷۰۴٫۸ و C ۸۱۳٫۶ بود. این ردیف‌ها بیشتر درباره‌ی LLVM در برابر GCC روی آن کد حرف می‌زنند تا سلام در برابر C. نوشته‌ی سه پشتیبان تولید کد سلام توضیح می‌دهد چرا پشتیبان این‌قدر اهمیت دارد.

ردیف‌های کتابخانه‌ای (۴۰ تا ۴۲) به دلیل مشابهی جدا گزارش می‌شوند. بسته‌ی matrix سلام ضرب ماتریس‌ها را در ۱۴٫۳ میلی‌ثانیه انجام داد، در برابر ۴۲٫۵ میلی‌ثانیه برای حلقه‌های ساده‌ی C، ولی در 41_matrix_stdlib_smallops سلام ۳۶٫۴ میلی‌ثانیه طول کشید در برابر ۴٫۱ میلی‌ثانیه برای C، جایی که gcc بیشتر کار حلقه را حذف می‌کند. این ردیف‌ها یک کتابخانه (matrix، tensor) را با یک حلقه‌ی دست‌نویس مقایسه می‌کنند، نه یک کامپایلر را با کامپایلری دیگر.

جلوتر، برابر و عقب‌تر

صفحه‌ی سرعت یک برنامه را برابر می‌شمارد وقتی زمان سلام کمتر از ۵٪، یا کمتر از نیم میلی‌ثانیه، با سریع‌ترینِ C، C++، Rust و Go در همان برنامه فاصله داشته باشد. فاصله‌هایی به این کوچکی از اجرایی به اجرای دیگر برعکس می‌شوند. با این قاعده، روی ۳۹ برنامه (زمان‌ها به میلی‌ثانیه):

  • جلوتر در ۶ برنامه: فاصله‌ی ویرایشی (۴٫۵ در برابر ۷٫۸ برای C)، بازی زندگی (۷٫۲ در برابر ۹٫۱ برای C)، تجزیه‌ی ریشه‌ای (۸٫۱ در برابر ۹٫۸ برای Rust)، جمع زیرمجموعه (۸٫۵ در برابر ۱۰٫۳ برای C)، طولانی‌ترین زیردنباله‌ی صعودی (۹٫۷ در برابر ۱۰٫۸ برای C) و مرتب‌سازی هرمی (۲۶٫۲ در برابر ۲۸٫۱ برای C).
  • برابر در ۲۷ برنامه، از جمله فیبوناچی تکراری، شمارش عددهای اول، غربال، مرتب‌سازی سریع، مرتب‌سازی ادغامی، مندلبرو، ضرب ماتریس و کار با رشته.
  • عقب‌تر در ۶ برنامه: عدد تصادفی LCG (۱۹٫۴ در برابر ۱۷٫۹ برای C)، شمارش حلقه (۵٫۹ در برابر ۵٫۳ برای C)، جستجوی دودویی (۶۶۷٫۷ در برابر ۵۸۳٫۹ برای Rust)، n وزیر (۴۱٫۱ در برابر ۳۵٫۷ برای C)، فیبوناچی بازگشتی (۳٫۱ در برابر ۲٫۴ برای C) و چاپ یک خط، یک میلیون بار (۸٫۲ در برابر ۲٫۶ برای Rust).

مقایسه در هر ردیف با بهترینِ چهار زبان است، پس «عقب‌تر» می‌تواند همچنان به معنای جلو زدن از سه تای آن‌ها باشد. انتخاب هم به سود سلام کار می‌کند: همان اجرا سلام را به چند شکل سنجید و صفحه‌ی سرعت ساخت LLVM آن را گزارش می‌کند که در آن اجرا سریع‌ترینِ آن‌ها بود. گزارش بهترین پیکربندی یک زبان در برابر تنها یک پیکربندی از زبان‌های دیگر، به آن زبان برتری کوچکی می‌دهد. پرونده‌ی benchmark/results/README.md در مخزن با اجراهای بعدی روی دستگاه‌های دیگر دوباره ساخته می‌شود و آن‌ها را به شکل دیگری خلاصه می‌کند: در هر ردیف بهترینِ چهار ساخت سلام را برمی‌دارد و برابری را با دقت یک میلی‌ثانیه‌ی کامل می‌شمارد، که قاعده‌ای سست‌تر از قاعده‌ی صفحه‌ی سرعت است. عددهای مطلق آن با صفحه‌ی سرعت فرق دارند و خلاصه‌اش به سود سلام‌تر است؛ قاعده‌ی سخت‌گیرانه‌تر صفحه‌ی سرعت همان است که باید نقل شود.

چرا برنامه‌های کوتاه گمراه‌کننده‌اند؟

بیشتر برنامه‌های این مجموعه بین ۲ تا ۳۰ میلی‌ثانیه طول می‌کشند و میانه‌ی زمان C ۱۰٫۵ میلی‌ثانیه است. در این مقیاس:

  • راه‌اندازی سهم واقعی هر عدد است. 01_fib_recursive مقدار fib(30) را حساب می‌کند و در C ۲٫۴ و در سلام ۳٫۱ میلی‌ثانیه طول می‌کشد؛ کسری از یک میلی‌ثانیه که پیش از تابع اصلی صرف می‌شود، نتیجه‌ی این ردیف را تعیین می‌کند.
  • سیاست ورودی و خروجی می‌تواند یک ردیف را تعیین کند. 12_hello_print یک خط ثابت را یک میلیون بار چاپ می‌کند: Rust ۲٫۶ میلی‌ثانیه، سلام ۸٫۲ و C ۱۷٫۷. این ردیف می‌سنجد هر محیط اجرا خروجی استاندارد را چطور میان‌گیری (Buffer) می‌کند، نه این‌که حلقه‌هایش چقدر خوب‌اند.
  • نوسان چند درصد است. هر زمان کمینه‌ی ۱۰ اجرا روی یک ماشین مجازی اشتراکی است، و فاصله‌های کمتر از حدود ۵٪ ممکن است در اجرای بعدی برعکس شوند.

هیچ‌کدام از این‌ها سنجش را بی‌فایده نمی‌کند. در جدا کردن زبان‌های کامپایلی از تفسیری خوب است، در گرفتن پسرفت سرعت کامپایلر از نسخه‌ای به نسخه‌ی بعد خوب است، و در پیدا کردن برنامه‌هایی که سلام آشکارا در آن‌ها عقب است و ارزش نمایه‌گیری (Profiling) دارند. در رتبه‌بندی زبان‌های کامپایلی‌ای که کمتر از ۱۰ درصد با هم فاصله دارند خوب نیست.

یک برنامه‌ی سنجش، کنار هم

این 27_edit_distance است، بهترین ردیف سلام در برابر C (۴٫۵ در برابر ۷٫۸ میلی‌ثانیه). فاصله‌ی لوِنشتاین را میان دو دنباله‌ی ساختگی ۲٬۰۰۰ نمادی با یک جدول برنامه‌ریزی پویای دوسطری حساب می‌کند. نسخه‌ی اصلی این برنامه در benchmark/programs/27_edit_distance/prog.salam به سلام انگلیسی است؛ آنچه این‌جا می‌بینید برگردان فارسی همان پرونده است، بدون سرآیند مجوز. ساختار، گام‌ها و حتی شکست خط‌ها همان است و فقط واژه‌های کلیدی، نام متغیرها و رقم‌ها فارسی شده‌اند؛ سلام فارسی و انگلیسی یک زبان‌اند با دو املای واژه‌ها، یک دستور زبان و یک کامپایلر:

سلام
تابع ریشه:
    طول۱ := ۲۰۰۰
    طول۲ := ۲۰۰۰

    ناپایا دنباله۱ := وکتور {} برگردان وکتور<صحیح۶۴> ناپایا ی := ۰
    تا ی < طول۱:
        دنباله۱.بیفزا(((ی برگردان صحیح۶۴) * ۳۱ + ۷) % ۲۶)
        ی++
    پایان

    ناپایا دنباله۲ := وکتور {} برگردان وکتور<صحیح۶۴> ناپایا ج := ۰
    تا ج < طول۲:
        دنباله۲.بیفزا(((ج برگردان صحیح۶۴) * ۱۷ + ۱۱) % ۲۶)
        ج++
    پایان

    ناپایا قبلی := وکتور {} برگردان وکتور<صحیح۶۴> ج = ۰
    تا ج <= طول۲:
        قبلی.بیفزا(ج برگردان صحیح۶۴)
        ج++
    پایان

    ناپایا جاری := وکتور {} برگردان وکتور<صحیح۶۴> جاری.صفرها(طول۲ + ۱)

    ی = ۱
    تا ی <= طول۱:
        جاری.بنشان(۰، ی برگردان صحیح۶۴)
        ج = ۱
        تا ج <= طول۲:
            ناپایا هزینه := ۰ برگردان صحیح۶۴
            اگر دنباله۱.بگیر(ی - ۱) == دنباله۲.بگیر(ج - ۱):
                هزینه = ۰ برگردان صحیح۶۴
            وگرنه:
                هزینه = ۱ برگردان صحیح۶۴
            پایان
            هزینه حذف := قبلی.بگیر(ج) + ۱
            هزینه درج := جاری.بگیر(ج - ۱) + ۱
            هزینه جایگزینی := قبلی.بگیر(ج - ۱) + هزینه
            ناپایا کمینه := هزینه حذف
            اگر هزینه درج < کمینه:
                کمینه = هزینه درج
            پایان
            اگر هزینه جایگزینی < کمینه:
                کمینه = هزینه جایگزینی
            پایان
            جاری.بنشان(ج، کمینه)
            ج++
        پایان
        ج = ۰
        تا ج <= طول۲:
            قبلی.بنشان(ج، جاری.بگیر(ج))
            ج++
        پایان
        ی++
    پایان

    سرچاپ قبلی.بگیر(طول۲)
پایان
خروجی1846

و هسته‌ی نسخه‌ی C از همان پوشه:

C
for (i = 1; i <= LEN1; i = i + 1) {
    cur[0] = i;
    for (j = 1; j <= LEN2; j = j + 1) {
        long long cost;
        long long del_cost;
        long long ins_cost;
        long long sub_cost;
        long long m;
        if (seq1[i - 1] == seq2[j - 1]) {
            cost = 0;
        } else {
            cost = 1;
        }
        del_cost = prev[j] + 1;
        ins_cost = cur[j - 1] + 1;
        sub_cost = prev[j - 1] + cost;
        m = del_cost;
        if (ins_cost < m) {
            m = ins_cost;
        }
        if (sub_cost < m) {
            m = sub_cost;
        }
        cur[j] = m;
    }
    for (j = 0; j <= LEN2; j = j + 1) {
        prev[j] = cur[j];
    }
}

این دو، گام به گام یک الگوریتم‌اند: همان جدول، همان سه هزینه، همان کپی سطر در پایان. (حلقه‌ی تا شرط: در سلام تا وقتی شرط برقرار است اجرا می‌شود؛ برنامه‌های سنجش به سبکی شبیه C نوشته شده‌اند و در چند جا دو دستور در یک خط آمده‌اند.) حدود چهار میلیون دور حلقه‌ی درونی در هر دو در چند میلی‌ثانیه تمام می‌شود. آنچه سنجش به شما نمی‌گوید این است که چرا نسخه‌ی سلام روی این دستگاه سریع‌تر است. ممکن است به خاطر تولید کد باشد، چیدمان حافظه، راه‌اندازی یا چیز دیگری؛ پاسخ به این پرسش یک نمایه‌گیر و ورودی‌های بزرگ‌تر می‌خواهد، نه جدولی از کمینه‌ی زمان‌ها. یک ردیف مانند این را دلیلی برای بررسی بیشتر بدانید، هرگز نتیجه‌گیری نهایی.

سنجش مسیریاب HTTP: سرعت وب‌سرور سلام

صفحه‌ی سرعت یک سنجش دوم و جداگانه را هم گزارش می‌کند: نه سرور HTTP که به همان یازده مسیر پاسخ می‌دهند. در ۶ آبان ۱۴۰۵ (۲۸ اکتبر ۲۰۲۶)، روی یک محیط اشتراکی با ۶ پردازنده‌ی مجازی AMD EPYC که سرورها روی ۴ هسته و ابزار تولید بار روی ۲ هسته‌ی دیگر قفل شده بودند، net/http سلام میانگین ۹۴٬۸۵۰ درخواست در ثانیه گرفت (میانگین هندسی روی ۱۱ مسیر)، در برابر ۶۲٬۴۹۳ برای چارچوب Fiber در Go، ۳۴٬۹۸۷ برای net/http استاندارد Go، ۲۲٬۵۳۶ برای Node با Express و ۱۱٬۹۹۲ برای سرور توکار PHP. بار، ۶۴ اتصال keep-alive بود، ۱۰ ثانیه برای هر مسیر پس از ۳ ثانیه گرم‌کردن، و مسیرها به‌نوبت روی همه‌ی سرورها سنجیده شدند.

این عددها را هم با همان دقت بخوانید:

  • میانگین بار دستگاه از کارهای نامربوط حدود ۱۱ بود. صفحه برای فاصله با Fiber دو عدد می‌دهد، ۱۶٪ روی دستگاه آرام و ۵۲٪ در اجرای کامل زیر بار، چون بار پس‌زمینه به سرورهایی که نخ‌های کمی نگه می‌دارند، از جمله سلام، کمک می‌کند.
  • در برخی مسیرها فاصله باریک است: روی / سلام با ۹۱٬۱۳۶ درخواست در ثانیه تنها ۴٪ از ۸۷٬۶۵۱ درخواست Fiber جلوتر بود. مسیر پرونده‌ی ایستا به همان اندازه که سرور را می‌سنجد، حافظه‌ی نهان را هم می‌سنجد، چون سلام پرونده‌های ایستا را پس از نخستین درخواست در حافظه نگه می‌دارد، در حالی که nginx در این پیکربندی پرونده را در هر درخواست باز می‌کرد.
  • ابزار سنجش و نتیجه‌های خامش در tests/en/apps/httpbench است. پرونده‌ی BENCHMARK.md آن اکنون اجرای بعدی‌ای را نگه می‌دارد که روی یک اجراکننده‌ی ۴ پردازنده‌ای GitHub انجام شد، و در آن فاصله با Fiber بسیار کمتر است و Fiber در برخی مسیرها جلوتر است، به‌روشن‌ترین شکل در مسیر پرونده‌ی ایستا. این دقیقاً همان حساسیت به سخت‌افزار و بار است که صفحه درباره‌اش هشدار می‌دهد.

خلاصه‌ی صادقانه این است که سرور HTTP سلام در این نوع آزمون هم‌رده‌ی سریع‌ترین چارچوب‌های Go است و به‌خوبی جلوتر از Node و سرور توکار PHP. این عددهای درخواست در ثانیه را با زمان‌های برنامه‌های بالا مقایسه نکنید و بدون اجرای دوباره روی سخت‌افزار خودتان، برنامه‌ریزی ظرفیت را بر پایه‌ی آن‌ها انجام ندهید. نوشته‌ی ساخت وب‌سرورهای سریع با سلام خود سرور را بررسی می‌کند.

چطور این سنجش را خودتان تکرار کنید؟

همه‌ی برنامه‌ها، اجراکننده‌ی سنجش (که خودش به زبان سلام نوشته شده) و ایمیج Docker که ابزارها را تعریف می‌کند، در مخزن هستند:

ترمینال
git clone https://github.com/SalamLang/Salam
cd Salam
docker compose -f benchmark/docker/docker-compose.yml run --rm bench
docker compose -f benchmark/docker/docker-compose.yml run --rm bench 27_edit_distance 12_hello_print

فرمان نخست هر ۴۲ برنامه را اجرا می‌کند و README.md، results.json و results.csv را در benchmark/results/ می‌نویسد؛ با نام بردن از برنامه‌ها فقط همان‌ها اجرا می‌شوند، و متغیر محیطی BENCH_RUNS تعداد اجراهای زمان‌دار را تغییر می‌دهد. هر اجرای تازه این پرونده‌ها را بازنویسی می‌کند، پس اجرایی که پشت صفحه‌ی سرعت است در تاریخچه‌ی مخزن با کامیت "benchmark: results for v0.6.0 (8cfe206)" (درخواست ادغام #2407) مانده است. برای سنجش HTTP، tests/en/apps/httpbench/bench/run.sh سرورها و ابزار تولید بار را می‌سازد و DURATION، CONNS، THREADS، SERVERS و ROUTES آن را کنترل می‌کنند.

برای رفتن فراتر از این مجموعه: دستگاهی آرام به کار ببرید، ورودی‌ها را آن‌قدر بزرگ کنید که هر برنامه یک ثانیه یا بیشتر اجرا شود تا زمان راه‌اندازی دیگر اهمیتی نداشته باشد، میانه و پراکندگی را کنار کمینه گزارش کنید، و هر ردیفی را که بیش از حد نوسان فاصله دارد پیش از توضیح دادن، نمایه‌گیری کنید.

پرسش‌های پرتکرار

آیا زبان سلام از C سریع‌تر است؟

نه به معنای کلی. روی ۳۹ برنامه‌ی هم‌الگوریتم پروژه، میانگین سلام ۱۰٫۶ میلی‌ثانیه و میانگین C ۱۱٫۷ میلی‌ثانیه بود؛ تفاوتی آن‌قدر کوچک که زمان راه‌اندازی و نوسان می‌توانند توضیحش دهند. سلام در چند برنامه، مانند n وزیر و فیبوناچی بازگشتی، از C عقب بود. برای تفاوت‌هایی که از سرعت مهم‌ترند، مقایسه‌ی سلام و C را ببینید.

آیا سلام از Rust سریع‌تر است؟

روی همان مجموعه، میانگین سلام ۱۰٫۶ میلی‌ثانیه و Rust ۱۱٫۳ میلی‌ثانیه بود، یعنی یک رده. Rust در جستجوی دودویی و چاپ یک میلیون خط آشکارا جلوتر بود، و این دو اغلب نزدیک هم قرار می‌گیرند چون هر دو از راه LLVM کامپایل می‌شوند. مقایسه‌ی سلام و Rust این دو زبان را فراتر از سرعت، و پیش از همه از نظر ایمنی حافظه، مقایسه می‌کند.

زبان سلام چند برابر سریع‌تر از پایتون است؟

روی این مجموعه حدود ۵۶ برابر (میانگین هندسی ۱۰٫۶ میلی‌ثانیه در برابر ۵۹۸ میلی‌ثانیه)، و حدود ۹٫۶ برابر سریع‌تر از PHP. این فاصله بزرگ و پایدار است، چون حاصل مقایسه‌ی کد کامپایل‌شده با یک مفسر است، نه تفاوت‌های کوچک میان کامپایلرها. مقایسه‌ی سلام و پایتون کارهایی را که پایتون هنوز بهتر انجام می‌دهد بررسی می‌کند.

آیا می‌شود به بنچمارکی که پروژه‌ی خود یک زبان منتشر کرده اعتماد کرد؟

تا جایی که بتوانید بررسی‌اش کنید. این سنجش برنامه‌ها، اجراکننده، ایمیج Docker و زمان خام تک‌تک اجراها را منتشر می‌کند، می‌گوید سلام کجا می‌بازد و محدودیت‌های خودش را نام می‌برد. اجرای دوباره‌ی آن روی سخت‌افزار خودتان دو فرمان است، و این بهترین راه برای برطرف کردن هر تردیدی است.

کامپایلر و سرعت

از کد منبع تا فایل اجرایی: کامپایلر زبان برنامه‌نویسی سلام مرحله به مرحله چگونه کار می‌کند

یک برنامه‌ی سلام را در مسیر کامپایلر دنبال کنید: توکن‌ها، درخت نحو، بررسی گونه‌ها و قاعده‌ها، کد C یا LLVM IR یا کد ماشینِ تولیدشده و پیوند نهایی، با خروجی واقعی هر مرحله.

خواندن مقاله
کامپایلر و سرعت

وقتی یک زبان خودش را می‌سازد: درون کامپایلر خودمیزبان زبان برنامه‌نویسی سلام

کامپایلر سلام به زبان سلام نوشته شده است. این روایت چگونگی رسیدن به این نقطه است: هر نسخه چطور از نسخه‌ی پیشین بوت‌استرپ می‌شود، خودمیزبانی برای یک زبان جوان چه سودی دارد و چه هزینه‌ای.

خواندن مقاله