مقایسه کامپایلر با مفسر : چرا زبان های کامپایلری بهینه تر هستند؟
حتما شنیده اید که زبانهایی مانند C و ++C نسبت به زبانهای مفسری همچون پایتون و Node.js بهینه تر هستند. در این مقاله سعی کردم که به شما نشان دهم که چرا زبانهای کامپایلری مانند C و ++C دارای کارایی بالاتری نسبت به زبانهای دیگر هستند. با این حال برای خواندن این مقاله باید پیش زمینه ای در مورد معماری کامپیوتر مدرن داشته باشید.
کارایی و قدرت یک پردازنده را می توان با نسبت تعداد اجرای دستورالعمل ها در هر سیکل ساعت سنجش کرد. دو روش اصلی برای اجرای دستورالعمل های موازی وجود دارد که می تواند IPC را ارتقا دهد.
عمق Pipeline
Pipeline ها شبیه به شستن لباس ها هستند :
- مقداری لباس کثیف را داخل ماشین لباسشویی قرار می دهید
- وقتی شستشوی لباس ها انجام شد، لباسهای خیس در خشک کن قرار میگیرند
- زمانی که مرحله خشک کردن انجام شد، لباس ها جمع آوری می شوند
- زمانی که مرحله جمع آوری به پایان رسید لباس ها در سر جایشان قرار خواهند گرفت
اگر خانواده بزرگی داشته باشید، مسلما چیزهایی مانند دوش و ماشین لباسشویی به منابع ارزشمندی برای شما تبدیل خواهد شد، زیرا باید برای استفاده از آن ها با دیگر اعضای خانواده رقابت کنید. مسلما شما منتظر نمی مانید تا خواهرتان تمام مراحل شستشو و خشک کردن و جمع آوری لباسهای خود را انجام دهد و سپس شما اقدام کنید.
می خواهید این کارها به صورت موازی انجام شود، به صورت مشابه وقتی دستورالعملی در حال اجراست میتوانیم دستورالعمل بعدی را قبل از اتمام دستورالعمل قبلی اجرا کنیم، ما این دستورالعمل ها را روی Pipeline قرار میدهیم.
برای کسانی که می خواهند بدانند در سطح سخت افزار دقیقا چه اتفاقی می افتد شکل بالا را نشان داده ایم. همین طور که مشاهده می کنید در حالت پایین، همزمانی دستورالعملها نشان داده شده است.
پهنای Pipeline
روش دیگر برای افزایش سطح IPC تکرار کامپوننت های درونی پردازنده یعنی ALU است. در این صورت پردازنده میتواند به صورت همزمان دستورالعمل های متعددی را در هر مرحله Pipeline اجرا کند. اگر بخواهیم این مورد را با مسایل خود سازگار کنیم به این معناست که یک لباسشویی و خشک کن دیگر به خانه خود اضافه کنیم.
فرض کنید که برنامه اسمبلی زیر را داشته باشید :
به دلیل وابستگی ببین دستورالعملها، بهترین حالتی که میتوانیم برای اجرای دستورالعمل ها انتظار داشته باشیم، به صورت زیر است :
هر تکرار از حلقه به ۴ سیکل ساعت، زمان نیاز خواهد داشت. اگر از سخت افزار اضافی تر استفاده کنید این مقدار را می توانید به عدد کمتری کاهش دهید.
باز کردن حلقه
اینجا دقیقا جایی است که کامپایلرها به میدان میآیند. کامپایلر ها حلقه ها را Unrol کرده و به عبارت دیگر آنها کد هایی که قرار است اجرا شوند را در طول حافظه پخش میکنند، در طی فرآیند باز کردن، کامپایلر رجیستر های دیگری را برای ذخیره سازی نتایج میانی معرفی می کند، که در آن هیچ وابستگی به داده ها وجود ندارد.
توجه داشته باشید که حالا ۱۲ دستورالعمل از ۱۴ دستورالعمل در حلقه به صورت جفت اجرا می شود. برای هر چهار تکرار حلقه، ۸ کلاک زمان نیاز است و یا به عبارتی دیگر به دو کلاک زمان برای هر تکرار نیازمندیم که در این صورت زمان اجرا کاهش قابل توجهی خواهد داشت.
نتیجه
در نتیجه کامپایلرها چیزی فراتر از چک کردن قواعد نوشتاری برنامه شما انجام می دهند، بر خلاف برنامه هایی که مفسری هستند (خواندن و اجرای آنها خط به خط انجام میشود) برنامههایی که کامپایل می شوند، می توانند بسیار بهینه تر از زبان های دیگر عمل کنند.
منبع: لرن سورس