خیر.
زبان Rust پرفرمنس «نزدیک به C» داره. نه لزوما بهتر از C
و فایل باینری خروجی از کامپایلر، حجمش از نسخهی C بیشتره (که نکتهی مهمی هست توی توسعهی کرنلها)
یه بخشیش به خاطر اینه که Rust اصولا static link هست، ولی اگه اینم درنظر نگیریم (یعنی کد Cرو هم به صورت static لینک کنیم) باز هم یه مقدار حجم خروجی rust بیشتر میشه.
مسائل دیگهای هم هست که میشه مقایسه کرد ولی درکل در هر زمینهای فکر میکنم همونطور که C++ نمیتونه جای C رو بگیره، Rust هم به همون دلایل نمیتونه جایگزین Cبشه.
قطعا جایگاه خوبی داره بین برنامهنویسها و احتمالا وضعیتش بهتر هم میشه. الآن هم یه کم بحثش داغ هست و یه جورایی ترند شده.
خلاصه توی منهنی Hype Chart فکر میکنم توی مرحلهی Early Adopters Investigate باشه. یعنی هنوز جا داره برای «فراز» و بعد احتمالا یه «فرود» پیش رو داره تا به پایداری برسه. (از نظر comunity و میزان استفاده)
یه چیز باحالی که توی C هست و خیلی کم بین زبانهای برنامهنویسی دیده میشه اینه که C، سرعت خودشو با اجرای تست و کانورت کردن دیتاها و اینجور چیزا پایین نمیاره و برنامهنویس رو فردی باهوش تصور میکنه که خودش حواسش هست گند نزنه
از طرف دیگه هستهی واقعا کوچیکی داره در حدی که شما یه printf میخواید انجام بدید، باید لایبرری stdio رو صدا بزنید چون توی خود C همچین چیزی (با اینکه یه system call ساده هست) طراحی نشده. (یه مثال کلی زدم، درحال مقایسه کردنش با زبان خاصی نیستم)
این باعث میشه که برنامهنویس ماکزیمم کنترل رو روی برنامه و نحوهی اجراش داشته باشه. خیلی نزدیک به کد زدن در زبان ماشین هست. البته کامپایلرها بهینهسازیهای زیادی روی کدهامون انجام میدن و ازشون کمال تشکر رو داریم! ولی منظورم اینه که کار اضافهای انجام نمیدن.
لینوس توروالدز هم اگه گفته «اوکی. اگه خیلی اصرار دارید، با rust ماژولتون رو بنویسید» فقط واسهی اینه که بهش گیر ندن و دست از سرش بردارن
وگرنه این توروالدزی که من میشناسم با اونهمه نفرتش نسبت به C++ عمرا نمیاد با rust کرنل رو بنویسه.
فکر میکنم It’s only a matter of time قبل از این که به نقطه ای برسیم که به خاطر سرعت توسعه و قابلیت اطمینان حتی برای برنامه نویسی کرنل هم سوییچ کنیم روی زبانهای سطح بالاتر، قبلا هم کارهای زیادی بوده که برای صد در صد بهینه بودن با c نوشته میشده ولی الآن دیگه نمیشه، چون سرعت پردازندهها انقدر بالارفته که عملا با trade-off بین سرعت اجرا و سرعت توسعه، میرسیم به زبانهای سطح بالا.
در اینکه برنامهنویسها تنبل شدن شکی نیست، هر چیزی که آدمهای زیادی واردش بشن، طبیعتا از میزان خفن بودنش کم میشه چون درصد انسانهای خفن، ثابته.
ولی اینکه شما برنامهنویسی کرنل رو با web development مقایسه میکنید، یه کم به نظرم غیر طبیعیه.
الآن ما ملیونها وبسایت داریم و یه شرکتی که داره وبسایت طراحی میکنه باید خیلی سریع کارشو تموم کنه وگرنه شکست میخوره.
اما کلا شاید ۱۰تا کرنل داشته باشیم و جامعهی هدف این کرنلها، انسانهای معمولی نیست. شرکتهایی هست که از این کرنلها استفاده میکنن تا مثلا سیستمعامل android تولید کنن بدن به انسانهای معمولی.
شما فرض کن یه کرنل داریم که با پایتون نوشته شده.
یه ماژول اثرانگشت که تولید میشه توسط یه شرکت چینی، بعد از ۲روز درایورش برای کرنل پایتونی نوشته میشه ولی برای لینوکس یک ماه طول میکشه تا توی پروسهی code reviewهای کرنل، به مرحلهای برسه که به نسخهی آخر کرنل اضافه بشه.
گوگل نمیاد به خاطر این مساله کرنل لینوکس رو بذاره کنار و اونی که با پایتون نوشته شده و سرعتش 0.1 لینوکس هست رو استفاده کنه.
دلایلش زیاده ولی دلیل اصلی اینه که یه گوشی موبایل، خیلی خیلی بیشتر از ۱ماه طول میکشه تا ساخته بشه و وارد بازار بشه.
این قضیه خیلی فرق میکنه با طرز فکر شرکتهایی مثل spotify که هیچ فرقی براشون نداره موزیکپلیرشون، ۱۰ مگابایت از رم کاربر مصرف کنه یا ۵۰۰ مگابایت!
من کی توسعه کرنل رو با وب مقایسه کردم
یه زمانی کرنلها با assembly نوشته نمیشدن؟ الآن دارن با c نوشته میشن، قطعا منظور من از استفاده از زبان سطح بالا، زبانیه که نسبت به c سطح بالاتره نه این که بگم یه دفعهای از c پرش میکنیم به python یا ruby
خب همین دیگه! (با صدای یه مجری تلوزیونی بخونید:) چرا برنامهنویسا از اسمبلی سویچ کردن به C و چرا به نظر من دیگه این اتفاق نمیفته؟ در ادامه خواهیم دید. با ما همراه باشید در اپیزود جدید DEV HEROES
اسمبلی، «دقیقا» زبان ماشینه که به جای ۰ و ۱ (یا کد hex) یه سری کلمه مینویسیم و اسمبلر دقیقا همونا (با یه سری استثنای خیلی کوچیک) رو تبدیل میکنه به باینری. (منظورم اینه که اسمبلی زبان نیست واقعا)
مشکلش چی بود؟ یه مشکل واقعی داشت! اونم این بود که هر ماشینی (هر cpuای)، زبان ماشین خودش رو داشت (و داره) پس اسمبلی خودشم داشت، پس باید برای هر CPU یه کرنل مینوشتیم!
اینجا Dennis Ritchie اومد C رو با این هدف ساخت که یه چیزی باشه خیلی نزدیک به اسمبلی، فقط کدهای ماشین متفاوتی تولید کنه برای cpuهای مختلف. و اصلا هدفش از ساخت C، کمک در توسعهی unix بود. نه اینکه بخواد یکی از خفنترین زبانها رو بسازه. فقط میخواست بتونن همون کدهای بهینه (که هیچ چیز اضافهای نداشت) رو تولید کنن برای چندتا CPU، بدون بازنویسی.
این، باعث شد که برنامهنویسها برن سمتش. یعنی تنها مزیتش این بود که میتونست کد ماشینهای مختلفی رو برای CPUهای مختلف تولید کنه.
بجز این، مزیت دیگهای نداشت (الآن مزایای زیادی داره از جمله بهینهسازی کد)
با هیچ زبان دیگهای (که من میشناسم) اولا نمیشه اسمبلی به این خوبی تولید کرد، دوما (که مهمتره) نمیشه یه خروجی واقعا minimal به دست آورد.
هر زبانی یه چیزی اضافهتر از کدی که برنامهنویس مینویسه به خروجی اضافه میکنه. حداقلش میشه همین چک کردن و free کردن حافظه، وقتی از یه scope خارج میشیم در زبان Rust
این چیزها میتونن توسعه رو راحتتر کنن، ولی راندمان رو پایین میارن. حتی نمیشه در مواقعی که میدونیم کامپایلر میتونست کار بهتری انجام بده، کد Rust رو تغییر بدیم که اسمبلی بهتری بسازه. چون حداقل من هرچی سعی کردم نتونستم از اسمبلی rust سردربیارم!
اینکه میگم اسمبلی که تولید میکنه واقعا بیشتر و پیچیدهتر از C هست، به چشم دیدم!
بگذریم.
با توجه به این چیزایی که گفتم، C رسما همون assembly هست. به خاطر همین هیچ چیز اضافهای توی باینری خروجی وجود نداره و اگر هم چیزی باشه که به نظرمون میتونست بهتر باشه، میتونیم فیکسش کنیم.
ولی زبانهای دیگه کدهای ما رو میبینن بعد هرچی دلشون میخواد کامپایل میکنن
یه blog post بود خیلیوقت پیش دیدم که حجم خروجی C و C++ و Rust و Golang رو با هم مقایسه میکرد توی یه برنامهی نسبتا ساده.
حتی C++ هم اگه یه cout و cin بخوایم توش بنویسیم حجم خروجیش چندبرابر C میشه که printf و scanf داشته باشه.
الآن بعد از کلی گشتن، لینکشو پیدا کردم ولی 404 شده بود. توی wayback machine میتونید ببینیدش.
توضیج: توی سرچهای بعدیم به این نتیجه رسیدم که iostream در C++ تستهای خیلی زیادی روی cin و cout انجام میده که توی stdio.h در C وجود نداره.
مرسی از توضیحات عالی و کاملت مثل همیشه
حقیقتش بعضی از چیزایی که گفتی رو اصلا در نظر نگرفته بودم. خیلی از دلایلی که ملت با c مشکل دارن و توی زبانهای دیگه حل شده عملا انگار از عمد توی c هست تا بتونه خیلی سریع و با حجم کد assembly کمتری کار کنه و این رو الآن دارم میفهمم.
چیز دیگهای که دارم بهش فکر میکنم اینه که خیلی از این موارد رو compiler هر چه قدر هم که هوشمند بشه نمیتونه کاریش بکنه چون برنامه نویس موقع نوشتن کد فرض کرده که زبان مورد نظرش توی runtime مثلا میاد و data type رو چک میکنه یا مثلا فلان مورد در مورد مموری رو بررسی میکنه و برنامش رو بر این مبنا نوشته، compiler مجبوره برای این موارد کد assembly تولید کنه.
خب اینکه مردم با C مشکل دارن، به خاطر اینه که برای کد زدن توی C شما باید memory management انجام بدید (منظورم malloc و free و بحثهای اون مدلی هست) و باید خیلی اطلاعات پایهای درمورد کامپیوتر رو داشته باشید مثل pointer و call by refrence و call by value. همچنین یه مشکل دیگه اینه که C واقعا هیچی نداره یعنی اگه من stack بخوام، باید خودم بسازم (مطمئن نیستم ولی باید لایبرریهایی وجود داشته باشه برای راحت کردن اینجور مسائل). پس باید درک درستی و کاملی از stack و tree و graph و… داشته باشم. برای پیادهسازی هرکدوم هم ۴تا روش بلد باشم با توجه به پروژم از یه روش استفاده کنم!
اینا چیزهایی هستن که اگه یه نفر بدونه، توی کار با هر زبانی، یه برنامهنویس بهتر میشه. ولی اگه ندونه هم میتونه با ruby کد بزنه.
در حالی که اگه اینا رو بلد نباشه توی C از hello world جلوتر نمیره
نمیشه گفت دونستن اینا فقط برای C لازمه، برای همهچی لازمه. ولی شما بگرد پیدا کن یه برنامهنویسی که توی سالهای اخیر با python و js کار خودش رو شروع کرده باشه و این چیزا رو بلد باشه!
دقیقا!
من الآن نمیتونم یه مثال با کد بزنم ولی توی ذهنم دارم به دوتا تابع فکر میکنم که یکی، اونیکی رو صدا میزنه بعد جواب رو میگیره و یه کاری باهاش میکنه و بعد میاد مموری رو با توجه به نیاز پروژه، اونجایی که دیگه بهش نیاز نداریم free میکنه. (این تصمیم رو برنامهنویس با توجه به معماری پروژه میگیره)
اینو میشه توی C به همین روشی که گفتم انجام داد. ولی مثلا Rust که به نظر من واقعا بین بقیهی زبانها بهترین memory management رو داره، این مدلیه که «خب از scope خارج شدیم، اینو free میکنم» بعد از یک میلیثانیه «خب وارد این scope شدیم یه فضا از ram میگیریم و با نامویاد خدا شروع میکنیم»
اگه بخواد هم نمیتونه درکی از معماری پروژه داشته باشه (چون این حرکت خیلی سطح بالاست و کامپایلر باید رفتار خودش رو زیر نظر داشته باشه!!). تازه ما با کلی بدبختی و زشت کردن سینتکس، باید بیایم lifetime تعیین کنیم برای یه متغیری، که بخشی از این رفتار رو مطابق معماری پروژمون اصلاح کنیم.
البته اینم بدیهای خودشو داره و همین بدیها باعث شدن نیاز به ساخت زبانهای سطح بالاتر به وجود بیاد. مثلا من باید یادم باشه (و کامنت بنویسم برای فردای خودم) که «مموری رو فلان جا گرفتم و باید حتما فلان جا آزاد بشه.»
ولی شما امید خودتو از دست نده. یه روز یه lisp میاد این مشکلا رو حل میکنه
دقیقا من این مشکلا رو با کسایی که با پایتون شروع به برنامه نویسی میکنن دارم، باید یک ساعت فقط بهشون توضیح بدم آرایه چیه و این که پایتون لیست داره و آرایه نداره یا این که باید توضیح بدم دیکشنری چیه؟ چرا مثل آرایه زمان دسترسی O(1) نداره.
یه بخشیش هم به منبعی که این زبانها رو یادمیگیرن ازش بر میگرده، شخصی که آموزش میده باید یه دید کلی نسبت به این مسائل بده.
در مورد Memory Management مسئله فقط سر دانش انجام دادن این کار نیست، باید بپذیریم که وقتی با C کد میزنیم و این کار رو خودمون انجام میدیم احتمال خطا به هر حال میره بالا، همین الآن حجم زیادی از attack هایی که به برنامههایی مثل whatsApp میشه مربوط به overflow هستش، یکیش همین چند وقت پیش یادمه اینجوری بود که برای تماس حجم بیشتر از چیزی که باید رو ارسال میکرد و دیتای اضافی روی گوشی فرد مورد حمله قرار گرفته (Victim) به عنوان کد اجرا میشد.
اوه! توضیح Big O notation واسه تازهکارها خودش اعصاب یعقوب میخواد
خب آموزشی هم که بخواد این چیزا رو یاد بده، کسی سمتش نمیره. همه دنبال «یادگیری C++ در ۱۰ روز» هستن. بعد اگه یه آموزش ببینن که نوشته «در ۵روز» فکر میکنن ضرر کردن.
دقیقا ۱۰۰٪ خطاهای مربوط به کامپیوتر مربوط میشه به PEBCAK. حالا یا بین کیبورد و صندلی خودمون، یا کیبورد و صندلی یه نفر دیگه (مثلا یه مشکلی توی یه لایبرری باشه و بدون تست پابلیش شده باشه)
این یکی از مفیدترین مطالبی بود که تا حالا خوندم.
همیشه فکر میکردم cycleهای CPU و تایم دسترسیش به cache خودش، با هم برابر باشه و فکر میکردم تایم دسترسی CPU به RAM هم خیلی نزدیکبه clock خود cpu باشه.
شاید به خاطر اینکه برنامهنویسی سطح پایین رو از منابع خیلی قدیمی یاد گرفتم و تمام تجربم روی microcontrollerها بوده که الآن نمیدونم آیا اونها هم اونطوری که فکر میکنم هستن یا مثل کامپیوترهای امروزی اختلاف خیلی زیادی بین کلاک cpu و دسترسی به ram وجود داره!
از اونجایی که این مطلب جای خودشو به 404 داد، من سعی کردم یه کم بهترش رو بازنویسی کنم. از اینجا میتونید ببینیدش:
متنش هنوز یه کم کار داره. فکر میکنم بازهم بشه با strip، حجم خروجیها رو کاهش داد. ولی هنوز توی کار با ستریپ مشکل دارم و یه وقتایی خروجی برنامه، غیرقابل اجرا میشه. باید ببینم دقیقا مشکل چیه.
برای عمیق تر شدن باید اینا رو بلد شد، من با python و c# شروع کردم ولی بعد دیدم دقیقا مشکلم اینه که همه چیز خیلی سادس، بعد اومدم سراغ c++ که چیزی مثل stack و call by reference و … رو یاد بگیرم.