چطور با  Node js و یک هسته، بهتر از چند هسته با قدرت یکسان نتیجه بگیریم ؟

چطور می توان برنامه ای نوشت که روی یک هسته اجرا شده و اتفاقا خیلی بهتر از چند هسته نتیجه بدهد ؟ اصلا همچنین چیزی ممکن هست؟یک دست صدا داره ؟جواب کوتاه : بله و خیر !و اما قصد دارم جواب بلند به این سوال را در این مقاله بررسی کنم. اول از همه باید این قید را در نظر بگیریم که قدرت سخت افزاری همه ی هسته ها یکسان است. جواب این سوال را اینطور می توان بیان نمود که کاملا بسته به مسئله ی روی میز دارد. اگر درمورد مسئله ای با محاسبات سنگین صحبت میکنیم به عنوان مثال ضرب دو ماتریس 1064 تایی درهم، جواب قطعا منفی خواهد بود.و این جواب در شرایطی که مسئله ی ما مربوط به I/O می شود متفاوت خواهد بود. بدیهی است که در بک اند وب اپلیکیشن ها و شمار زیادی از اپلیکیشن های موبایل بیشترین تمرکز روی API ها می باشد. تعریف API چیست ؟ خیلی ساده می توان گفت API فصل مشترکی برای تعامل دو نرم افزار است. یکی از کاربرد های API ارسال و دریافت پیام و دیتا از سرور می باشد.دوباره به مسائل مطرح شده نگاهی بیندازیم ، یک هسته میتواند در مدیریت درخواست های ورودی و خروجی موثر تر از چند هسته عمل کند و شمار زیادی از اپلیکیشن ها بر پایه ی همین ارتباطات ورودی و خروجی با سرور هستند. در نتیجه در اکثر سرور ها مدیریت بهینه ی درخواست های ورودی و خروجی مد نظر است. حال به بررسی چکونگی انجام اینکار در Node js و ساختار آن میپردازیم.:ساختار کلی Node jsنود یک موتور برای اجرای اسکریپت های جاوا اسکریپت در سرور می باشد. می دانیم js زبانی ذاتا تک نخ (single thread) هست و در نتیجه node نیز چاره ای به جز single thread بودن ندارد. و همین ارث بری از js یک ویژگی مهم دیگر نیز در اختیار ما قرار می دهد که به آن اصطلاحا معماری اتفاق گرا یا event driven architecture می گونید. فرض نمایید به محض اجرای برنامه یک حلقه ی بی پایان شروع به کار میکند و هر اتفاق را شنیده و پاسخ متناظر به آن را میدهد. شکل کلی همچین ساختاری شبیه به زیر خواهد شد :ساختار اتفاق گر با مرکزیت event loopهمانطور که مشاهد میکیند، هر event پس از شنیده شدن وارد کد listener شده و instruction ها تحویل سیستم عامل (thread pool) می شوند. پس از اتمام پروسه ایونت تمام شدن از سمت سیستم عامل شنیده شده و نتیجه در صورت لزوم به سطح اپلیکیشن انتقال می یابد.این ساختار ساده و در عین حال قدرتمند باعث می شود node js قادر به اجرای دستورات به شکل none-blockingباشد. سعی میکنم با مثالی ساده این مفهوم را انتقال دهم.سیستم سفارش گرفتن در یک رستوران را فرض نمایید ، ابتدا با فرض multi threadو blockingبودن مسئله را بررسی میکنیم. در این مثال ما 30 نفر نیرو قرار داده ایم تا به شکل باجه ای به مشتریان سرویس بدهند اما با این قید که در هر باجه وقتی مشتری سفارش می دهد تا آماده شدن سفارش آن باجه مشتری دیگری را قبول نخواهد کرد(blocking). در این حالت اگر تعداد مشتری همزمان از 30 نفر تجاوز نکند هیچوقت صف درخواست های ورودی را نخواهیم داشت ولی در صورت زیاد شدن مشتری ها صف های طولانی شکل خواهد گرفت.این کدوم دیدگاهه ؟حال با دیدگاه single thread و none-blockingبودن (درست مثل node js) به بررسی مثال خواهیم پرداخت. در این سناریو ما یک باجه در نظر گرفتیم با این تفاوت که وقتی مشتری سفارشی ثبت میکند ، بلافاصله سفارش مشتری بعدی ثبت می شود(none-blocking). در این دیدگاه سفارش ها و مدیریت آن ها به آشپزخانه(سیستم عامل و استخر نخ) سپرده می شود. با فرض تعداد کم ، مشتری ها سفارش را ثبت کرده و وارد صف انتظار برای آماده شدن می شوند. با فرض تعداد بالا در قیاس با دیدگاه قبل صف ورودی و انتظار برای ثبت سفارش تا حد قابل قبولی کمتر خواهد شد.حتما این سوال برای شما پیش آمده که چرا هر دو دیدگاه را با هم استفاده نکنیم، یعنی چند بادجه با دیدگاه none-blocking کار کنند. متاسفانه محدودیت های جدی مانع از راحت انجام شدن این کار می شوند . اما خبر خوب اینکه node js از ورژن 8 به بعد با ماژول cluster تا حدی این کار را انجام داد تا بتواند از همه ی هسته های CPUاستفاده نماید و در ورژن 12 از worker thread ها رونمایی شد تا بیشتر به این هدف نزدیک شده باشد. در پارت های بعدی مقاله بیشتر از قبل وارد کد شده و با ماژول cluster و worker thread ها در node js آشنا خواهیم شد.نتیجه گیریاگر مسئله محاسباتی سنگین باشد بدون شک چند هسته ای میتواند کمک بزرگی باشد و درصورتی که مسئله به ورودی و خروجی وابسته باشد می توان با دیدگاه none-blocking و استفاده از یک هسته نتیجه ی مطلوب تری نسبت به چند هسته همزمان گرفت.