kernel time :
اگه Task Manager را باز کنی و در سربرگ Performane و در قسمت Cpu را انتخاب کنی و در قسمتی که نمودار کارکرد پردازنده را داره رسم میکنه ، کلیک راست کنی ، یه گزینه ای بنام show kernel time وجود داره که اگه تیک بزنیش ، در قسمت دیاگرام و نمودار پردازنده (جایی که کارکرد و مقدار اشغال پردازنده را رسم میکنه) ، یه نمودارِ آبیِ پر رنگ تر ، در قسمت پایین ترِ اون نمودار رسم میکنه که بهش kernel time میگن . این نمودار هم مثل کارکرد cpu ، مدام در تغییر و بالا و پایین تر رفتن هست .
kernel time ، مقدار پردازشی که اون هسته ی منطقی از کدهای سیستم عامل و درایورها انجام میده را رسم میکنه .
مثلا در برنامه ی سی شارپ ای که نوشتی ، خود سی شارپ که با پردازنده در تماس نیست و کدهاش را تحویل پردازنده نمیده . (اصلا وقتی برنامه ای که در سی شارپ نوشتی را اجرا کنی ، دیگه بعد از اون سی شارپ معنا نداره . اونجا دیگه زبان clr هست که کدهای سی شارپ ات را باید تفسیر و کارهای مربوط بهش را انجام بده) .
اگه اشتباه نکنم ، clr با سیستم عامل در تماس هست و سیستم عامل هست که مستقیما با اون منبع سخت افزار مثل پردازنده در تماس هست .
بنابراین علاوه بر کدهای خودت که در سی شارپ نوشتی ، کدهای اضافه تری مثل کدهای clr و همچنین کدهای سیستم عامل هم باید اجرا بشه . اون نمودار kernel time ، این کدها را نشون میده (احتمالا باید کدهای clr هم جزء اش باشه . دقیق نمیدونم). یعنی وقتی برنامه ای که اجرا میشه ، کدهای بخش سیستم عامل و درایورهایی که برای اجرای اون کد از برنامه اجرا میشه را نشون میده .
بنابراین ، هر چی که نمودار kernel time ، کمتر باشه ، یعنی کدهای مربوط به سیستم عامل (و بقیه مثل درایور و اگه برنامه ی دات نت باشه ، احتمالا کدهای clr) کمتری اجرا شد و در عوض کدهای اون برنامه ، بیشتر اجرا شد .
در واقع ، یعنی کدهای برنامه ای از همه بهینه تر هه که کدهای بخش kernel time کمتر (نمودارِ آبیِ پر رنگ ، کمتر) و بخش کدهای مربوط به اون کد از برنامه ، یعنی بخش نمودار آبیِ کم رنگ ، بیشتر باشه .
اگه کدی که در پست قبلی دادم را چک کنی ، میبینی کدهای kernel time اش بصورت میانگین زیر 10 درصد هست (که البته متغییر هه . از 5 تا 15 درصد) . یعنی بقیه اش داره کدهای برنامه ی ما اجرا میشه . که این ، یک نمونه ی خیلی خوب از برنامه هست و یعنی کدها بهینه هست (البته ، این که فقط حلقه ی خالی هه . کلا میخوام بگم که کدی که این جوری باشه ، بهینه هست) .
البته دقت کن که kernel time به معنای توقف و سربارهای اون هسته ی منطقی نیست . یعنی نمیگه که اون هسته ی منطقی ، چقدر از زمانش را هدر داد . بنابراین ، منظورم از بخش بالا که گفتم "یعنی بقیه اش داره کدهای برنامه ی ما اجرا میشه" ، این نیست که پردازنده سربار نداره (یا سربارش کم یا زیاد هست) . بلکه به این معناست که در اون لحظه ای که اون هسته ی منطقی داره کد برنامه مون را اجرا میکنه ، بصورت میانگین ، فقط 10 درصد داره کدهای سیستم عامل (و کلا کدهایی که بصورت مستقیم به برنامه ی ما ربط نداره و ما ننوشتیم) را اجرا میکنه و بقیه اش کدهای ما را اجرا میکنه .
اما معمولا اگه یه هسته ی منطقی ای سربار زیادی داشته باشه (که به معنای هدر رفت و از دست دادن زمان برای اون هسته ی منطقی هست) ، نمودار کارکرد در Task Manager ، معمولا هی کم و زیاد میشه . و کمتر به حداکثرِ کارکردش ، یعنی به 100 در صد نزدیک میشه . چون مدام در حال سوئیچ بین نخ هاست . چون کدهای یک نخ (متد یا متغییر) ، هنوز در حافظه ی رجیسترش بارگذاری نشد که تا رسیدنش ، میتونه به نخ دیگه سوئیچ و اجرا کنه . البته اینی که گفتم (همین پاراگراف . درباره ی تغییرات بیشتر در نودار Task Manager ، احتمال سربار بیشتر) ، سند واسش ندارم . تحلیل خودم هست . فکر نکنم اشتباه باشه . دقیق نمیدونم .
یه مثال دیگه :
اول ، یه کنترل listbox بنام listBox2 را در فرم ات اضافه کن و همچنین یه کنترل TransparentControl که بهت دادم را بنام TransparentControl4 اضافه و رویداد کلیک اش را به متد TransparentControl4_Click در کد زیر متصل کن و کد :
کد:private void TransparentControl4_Click(object sender, EventArgs e) { this.listBox2.Items.Clear(); Application.DoEvents(); Thread thread = new Thread(new ThreadStart(this.NewThreadMethod)); thread.Start(); for (int i = 0; i < int.MaxValue; i++) { this.listBox2.Items.Add("Main Thread . i = " + i.ToString()); Application.DoEvents(); } } private void NewThreadMethod() { for (int counter = 0; counter < int.MaxValue; counter++) { this.Invoke(new AddToListBoxInMainThreadDelegate(this.AddToListBoxInMainThread), counter); Application.DoEvents(); } } private delegate void AddToListBoxInMainThreadDelegate(int parameters); private void AddToListBoxInMainThread(int parameters) { this.listBox2.Items.Add("New Thread . counter = " + parameters.ToString()); }
در این کد ، سوئیچ نخ ها را بهتر و قشنگ تر درک میکنی .
در کنترل listBox2 ، هر جا اول متن ، "Main Thread" نوشته شد ، یعنی اون حلقه ای که در نخ اصلی بود اجرا شد و هر جا اول متن ، "New Thread ......." نوشته شد ، یعنی حلقه ای که در نخ جدید بود (نخ ای که در متغییر thread ذخیره شده) ، اجرا شد .
همونطور که میبینی ، درهم و برهم این پیام ها را میده . یه بار مثلا 50 بار پیام "Main Thread" (اول پیام) را میده ، بعدش مثلا 10 بار پیام "New Thread ......." (اول پیام) را میده . یه وقت 2 بار اینو میده و .... .
همونطور هم که میدونی هر بار پیام ها که عوض میشن ، یعنی در اون لحظه ، به اون نخ سوئیچ کرده بود .
حالا ، اگه کد بالا را اجرا کنی ، نسبت به کد در پست قبلی ، با اونکه هر دو کد ، 2 نخ دارند و در دو نخ اجرا میشن ، چند تغییر را در Task Manager میبینی :
1) اول اینکه نسبت به کد قبلی ، نمودارِ کارکردِ هسته (های) منطقی (در Task Manager) ، خیلی بیشتر (از کد قبلی) بالا و پایین میره و خیلی نوسان بیشتری نسبت به کد قبلی داره و حتی به کارکرد حداکثر خودش که 100 درصد باشه نمیرسه (بسته به پردازنده داره ها ولی برای من ، کارکرد اون هسته ی منطقی ، تا 70 درصدش میره) .
قبل از توضیح دلیل این مسئله ، اینو بگم که در پست قبلی من گفته بودم که وقتی کدی اجرا میشه ، پردازنده با تمام توان و فرکانس اش (یعنی با فرکانس 100 درصد اش) اون کد را اجرا میکنه اما اگه این طور بود ، پس توی Task Manager ، کارکرد اون هسته ی منطقی را همیشه باید تا 100 درصد نشون میداد اما ما خیلی اوقات میبینیم که کارکرد پردازنده مثلا در اوقات بیکاری 10 درصد هست یا مثلا وقتی همین کد (در این پست) را اجرا میکنیم ، مثلا در پردازنده ی من ، 70 درصد از یه هسته ی منطقی اشغال هست (و 30 درصدِ بقیه اش خالی هست) یا در بهینه ترین کد هم خیلی کم میبینیم که عملکرد اون هسته ی منطقی بیشتر از 98 درصد بره . پس دلیل اینها چیه؟
دلیل اینها ، سرعت آپدیت بسیار پایینِ Task Manager هست . در واقع اون هسته ی منطقی وقتی بخشی از کد یک نخ را اجرا میکنه ، در چند میکروثانیه و دیگه خیلی زیاد در کمتر از چند میلی ثانیه انجام میده و بعد سوئیچ میکنه روی نخ دیگه اما بیشتر سرعت آپدیت Task Manager حدود 500 میلی ثانیه (0.5 ثانیه) هست که بین این اختلاف زمانیِ 0.5 ثانیه ، پردازنده بارها و بارها سوئیچ انجام داده .
در واقع ، Task Manager ، میانگین عملیات را حساب میکنه نه لحظه ی واقعی و دقیق عملکرد اون هسته ی منطقی را .
حالا در جواب سئوال شماره ی 1 که دلیل نوسان بیشتر این کد نسبت به کد قبلی بود ، در ختم کلام ، سوئیچِ بیشترِ این کد نسبت به کد قبلی هست در هسته ی منطقی هست . اما چرا سوئیچ در این کد بیشتر هه؟
مهمترین دلیلش اینه که کنترل ها (مثل button و list box و اینها) ، اگه بخوایم عملیاتی در اونها انجام بدیم مثلا در کنترلی ، رسم ای (نوشتن و تغییر متن) انجام بدیم و کلا با کنترلی کار کنیم ، مجبوریم فقط درونِ هر نخ ای که اون کنترل را ایجاد کردیم ، این عملیات (مثلا تغییر متن یا همون رسم) را انجام بدیم (چون ساختار کنترل ها از اول این جوری طراحی کردن که در یک نخ فراخونی بشه و در یک نخ کار کنه) . و از اونجایی هم که همه ی کنترل ها را به کنترل form1 مون اضافه میکنیم ، بنابراین مجبوریم وقتی با بقیه ی کنترل ها کار میکنیم ، درون همون نخ ای کار کنیم که کنترل form1 مون را ایجاد کردیم و چون کنترل form1 را درون متد Main (در کلاس Program) ایجاد کردیم و این متد Main هم همیشه درون نخ اصلی اجرا میشه (clr ، متد Main را فراخونی و در نخ اصلی اجرا میکنه) ، بنابراین مجبوریم با هر کنترلی که کار میخوایم کنیم ، کارِ مربوط به اون کنترل را درون نخ اصلی انجام بدیم .
بنابراین ، اگه درون هر نخ ای هم که باشیم ، مجبوریم برای کار با اون کنترل (گفتم دیگه . مثل رسم ، فراخونی اغلب متدهای مربوط به اون کنترل و ...) ، اون را در نخ اصلی فراخونی کنیم .
برای این کار (اینکه از درون یک نخ ، یه کاری را درون نخ ای که اون کنترل در اون ساخته شد که همون نخ اصلی هست ، انجام بدیم) ، باید از متد Control.Invoke استفاده کنیم .
بنابراین کدی که در نخ جدید (یعنی متد NewThreadMethod) نوشتیم ، در حلقه ی این متد ، نمیتونیم بگیم رسم (نوشتن متن) ای را به کنترل listBox2 اضافه کنه چون این متد ، در نخ اصلی قرار نداره (توضیح دادم دیگه کنترل ها در نخ اصلی کار میکنن) و باید با متد this.Invoke ، کد مربوطه اش را در متدی بنویسیم که اون متد در نخ اصلی فراخونی بشه . بنابراین ، متد AddToListBoxInMainThread (که متد this.Invoke ، متد AddToListBoxInMainThread را فراخونی میکنه) ، در نخ اصلی اجرا میشه .
به عبارتی دیگه ، در حلقه ی نخ جدید (در حلقه ی متد NewThreadMethod) ، هر بار کد را که اجرا میکنه ، مجبوره سوئیچ کنه به نخ اصلی و کد را در نخ اصلی انجام بده (این سوئیچ ، ممکنه در همون هسته ی منطقی ، یا هسته ی منطقیِ دیگه رخ بده) که همونطور که در پست قبلی توضیح داده بودم ، مثلا 5 میکرو ثانیه ، کد را اجرا میکنه و 50 میلی ثانیه را صرف سوئیچ نخ میکنه (چون سوئیچ را به ازای اجرای هر بار در حلقه ی متد NewThreadMethod ، حتمی و اجباری کردیم) .
نکته ی مهم دیگه اینه که چون هیچ نخی را همزمان ، 2 تا هسته ی منطقی اجرا نمیکنه و این در حالی هست که قبلا در یک هسته ی منطقیِ دیگه ، مشغول اجرای نخ اصلی در برنامه مون هست ، پس وقتی کارِ یه هسته ی منطقی دیگه در نخ جدیدمون (یا NewThreadMethod) تمام شد ، یعنی وقتی به کد this.Invoke در حلقه ی این نخ جدید (در متد NewThreadMethod) رسید و بنابراین میتونه به نخ اصلی برنامه مون سوئیچ کنه ، اما این سوئیچ ای را هم که سربار هم داره ، انجام نمیده چون در همین لحظه (ممکنه) هسته ی منطقی دیگه در حال اجرا کردن نخ اصلی مون باشه . و همزمان 2 نخ ، در دو هسته ی منطقی اجرا نمیشه چون ترتیب اجرای کدهای اون نخ ، بهم میخوره .
یعنی عملیات سربار ، خودش اتلاف وقت و انرژی از اون هسته ی منطقی میکنه اما این مشکل که باز هم اون هسته ی منطقی ای که نخ جدیدمون را پردازش کرد ، نمیتونه (ممکنه نتونه) به نخ اصلی مون سوئیچ کنه (چون ممکنه هسته ی منطقی دیگه ای در حال اجرای کد نخ اصلی برنامه مون باشه) که غوز بالای غوز میشه برای مشاهده این لینک/عکس می بایست عضو شوید ! برای عضویت اینجا کلیک کنید
اینکه کارکرد اون هسته ی منطقی چرا تا آخر نمیره را هم که گفتم .
2) سئوال دوم ای که پیش میاد اینه که موقع اجرای این کد ، اگه در Task Manager ، در سربرگ Details ، عملکرد پروسه ی مون را ببینی ، میبینی که در حد یه هسته ی منطقی (شاید یه کم بیشتر) داره پردازنده را اشغال میکنه . یعنی مثلا در پردازنده ی i5 4460 (چهار هسته ی فیزیکی و 4 هسته ی منطقی) ، در حد یه هسته ی منطقی (و یه کم بیشتر) یعنی 25 درصد (در واقع بین 20 تا 30 درصد) اون هسته ی منطقی را اشغال میکنه که مدام در حال تغییر هست .
در صورتی که ما دو نخ ساخته بودم و هر نخ هم (ممکن بود) در هر هسته ی منطقی مجزا اجرا بشه پس بنابراین مثل کد قبلی ، این کد هم به طبع ، مثلا در پردازنده ای که 2 هسته ی فیزیکی داره ، پروسه مون باید 100 درصد از اون پردازنده را مشغول میکرد (یا در پردازنده ی ای مثل 4460 ، باید 50 درصد از اون پردازنده را مشغول میکرد ولی چرا حداکثر 30 درصدش را مشغول میکنه؟) . چرا مثلا در پردازنده ی دو هسته ای ، دو هسته ی منطقی را مشغول نمیکنه؟ دلیل این چیه؟
چون همونطور که توضیح دادم ، با هر اجرای حلقه در نخ جدید (و اجرای متد this.Invoke در این نخ) ، به نخ اصلی باید سوئیچ کنه . هر نخ (مثل نخ اصلی) را هم در یک لحظه ، فقط درون یک هسته ی منطقی میتونه اجرا بشه .
اضافه کردنِ (عدد 1) به مقدار متغییر counter در نخ جدید که نسبت به اجرای کد this.Invoke در این نخ و بنابراین سوئیچ به نخ اصلی که زمان بسیار ناچیزی حساب میشه بنابراین همونطور که گفته بودم ، بخش عمده را داره بخاطر سربار سوئیچ به نخ اصلی (با اجرای کد this.Invoke) از دست میده .
بنابراین اغلبِ کدها ، بیشتر در نخ اصلی انجام میشه (در سئوال بعدی یعنی سئوال 3 ، بیشتر به این قضیه میپردازیم) و همونطور که گفتم ، هر نخ (مثل این نخ اصلی) ، در یک لحظه ، فقط در یک هسته ی منطقی اجرا میشه . بنابراین در این کد ، بیشتر اوقات (منظورم کل اوقاتش نیست)، فقط یک هسته ی منطقی در حال اجرای کد پروسه ی ماست .
نوسان ای که داره (بین 20 تا 30 درصد در مثلا پردازنده ی 4460 ، مدام در حال نوسان هست) هم بخاطر این سوئیچ های مدام بین این دو نخ هست که در بالا توضیح داده بودم .
3) وقتی این کد اجرا میشه (در Task Manager) ، نمودار kernel time ، بسیار بسیار بالا میره و در حد 60 درصد میره در صورتی که کل کارکرد اون هسته ی منطقی 70 درصد هم نیست (یعنی کدهای clr و مخصوصا سیستم عامل ، 60 درصد از زمان را صرف اجرا میکنه و کدهای (خالص که خودمون نوشتیم) برنامه ی ما ، فقط 10 درصد از زمان را صرف اجرا در اون هسته ی منطقی میکنه) . چرا؟
بخش عمده ی این قضیه برمیگرده به کد رسم (نوشتن متن) در کنترل . در واقع رسم در یک کنترل ، عملیات سنگینی هست که چون کنترل ها ، در واقع (در پشت پرده) با کمک توابع api های سیستم عامل هستند که ساخته (و رسم) میشه بنابراین علاوه بر اجرای کدهای سیستم عامل که باعث میشه kernel time بالا بره ، جزء توابع سنگین به حساب میان . یعنی منابع رم و بقیه ی منابعِ زیادی نسبت به کد ساده ی پست قبلی برای اجرا میطلبن.
بنابراین کد رسم ، یعنی کد this.listBox2.Items.Add که در متد AddToListBoxInMainThread (که این متد در نخ اصلی اجرا میشه) ، اجراش سنگین هست (و به طبع ، انتظار در هسته ی منطقی را بالاتر میبره) و همینطور کد Application.DoEvents که در هر بار اجرای حلقه ، اجرا میشه تا سیستم عامل را وادار که تا فورا و در همون لحظه ، همه ی پیام های (یا همون رویدادهای) مربوط به اون پروسه را برای اجرا ، به پردازنده ارسال کنه .
حالا این کد را جوری میخوایم بنویسیم که زمان اجرای کد در هم نخ اصلی و هم نخ جدیدش را بگیریم . منتها این بار ، تعداد اجراش را خیلی کمتر کنیم . مثلا تعداد اجرای حلقه ها در هر دو نخ را 20000 بار میکنم (زمان ، بر حسب میلی ثانیه هست) :
کد:private void TransparentControl4_Click(object sender, EventArgs e) { this.listBox2.Items.Clear(); Thread thread = new Thread(new ThreadStart(this.NewThreadMethod)); thread.Start(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 20000; i++) { this.listBox2.Items.Add("Main Thread . i = " + i.ToString()); Application.DoEvents(); } long elapsedTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(elapsedTime.ToString() + " mili secand", "Main Thread Loop Finished"); } private void NewThreadMethod() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int counter = 0; counter < 20000; counter++) { this.Invoke(new AddToListBoxInMainThreadDelegate(this.AddToListBoxInMainThread), counter); Application.DoEvents(); } long elapsedTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(elapsedTime.ToString() + " mili secand", "NewThreadMethod Loop Finished"); } private delegate void AddToListBoxInMainThreadDelegate(int parameters); private void AddToListBoxInMainThread(int parameters) { this.listBox2.Items.Add("New Thread ....... counter = " + parameters.ToString()); }
برای حساب کردن زمان ، بین این دو عدد پیامی که میده ، اونی را حساب کن که عددش بالا تر هه (زمان بر حسب میلی ثانیه هست) .
در پردازنده ی 4460 ، این کد بین 9.8 تا 10.8 ثانیه طول میکشه (هر بار ، متغییر هست ولی کلا بین این بازه هست) .
این کد ، توی هر پردازنده ی دیگه ای هم اجرا بشه (حتی پردازنده های 15 سال پیش . حتی پردازنده ای که یک هسته ی فیزیکی فقط داشته باشه) فکر نکنم اجراش بیش از 25 ثانیه طول بکشه .
همونطور که توی هر پردازنده ی جدیدی و مدرن ای با هر تعداد هسته ای اجرا بشه (مثل پردازنده ی i9-9900K که کلاک تک هسته ایش 5ghz هست یا پردازنده ی threadripper 3970x) فکر نکنم زودتر از 5 ثانیه اجرا بشه .
قیاس ها را نسبت به پردازنده ی 4460 انجام میدم .
به هر حال ، مهم اینه همیشه ، اجرای کدهای در چند نخ و در چند هسته بصورت همزمان ، نتیجه ی خوبی نداره . مثل همین کد .
اگه این کد را در یک نخ انجام بدی ، میبینی که زودتر از حالتی که در 2 نخ انجام دادی ، انجام میشه . دلیلش هم که گفتم (بخاطر سربار بسیار زیادی که پردازنده را مجبور میکنیم و همینطور دلایل دیگه که کامل در همین پست ، در بالا توضیح دادم) .
بنابراین این کد را در یک نخ اجرا میکنیم تا نتیجه را نسبت به زمانی که در دو نخ اجرا کرده بودیم ، ببینیم . دقت کن کد قبلی ، در دو نخ که هر کدوم از نخ ها ، حلقه ای داشتن که 20000 بار تکرار میشد . بنابراین وقتی که کد دو نخ را بخوایم یکی کنیم ، یک حلقه ای باید داشته باشیم که دو برابر تعداد حلقه هاش باشه . یعنی تعداد حلقه هاش 40000 بار باشه :
کد:private void TransparentControl4_Click(object sender, EventArgs e) { this.listBox2.Items.Clear(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 40000; i++) { this.listBox2.Items.Add("Main Thread . i = " + i.ToString()); Application.DoEvents(); } long elapsedTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(elapsedTime.ToString() + " mili secand", "Main Thread Loop Finished"); }
این کد (که تک نخی هست) ، در پردازنده ی 4460 ، بین بازه ی 8.3 تا 9 ثانیه ، اجرا میشه .
در صورتی که کد قبلی که 2 نخی بود ، بین 9.8 تا 10.8 ثانیه در این پردازنده اجرا میشد .
یعنی حداقل 0.8 ثانیه تا حداکثر 2.5 ثانیه (میانگین ، 1.8 ثانیه) زودتر از کد قبلی (که 2 نخی بود) اجرا میشه .
مشخص هست هر چی تعداد حلقه زیادتر بشه ، این اختلاف زمانی بیشتر خودش را نشون میده .
پس همیشه ، ساختن چند نخ و اجری همزمان در چند هسته (ی منطقی) ، باعث نمیشه کدها زودتر اجرا بشن . (وقتی کدها را در چند نخ اجرا کنیم) ، گاها بجای سریعتر اجرا شدن ، فرقی با سرعت اجرا در یک نخ نمیکنن ، گاها مثل این کد ، دیرتر اجرا میشن و گاها هم بسیار بسیار دیرتر اجرا میشن (این نمونه را در مثال بعدی در پست بعدی میبینیم) .
بنابراین این کاملا به طرز کدنویسی برنامه نویس و مخصوصا اینکه چقدر با کارکرد پردازنده ها آشناتر باشه و بدونه کدهاش در چه وضعیتی در پردازنده ها قرار میگیرن و مخصوصا اینکه بتونه الگوریتم ای طراحی کنه که بتونه کدهاش را جوری تقسیم کنه که با هم تداخل نداشته باشن و دو نخ ای که ساخت ، هر دو نخ ، در یک زمان ، به اطلاعات داده ای (مثلا متغییر) مشترکی نخوان (با هم) دسترسی داشته باشن ، داره .
به این دو کد نگاه نکن . اینها بسیار ساده هستند . هماهنگی بین نخ ها که باعث بهینه تر شدن کدها میشه ، بسیار بسیار کار پیچیده ای هست (منظورم اینه که جوری الگوریتم طراحی بشن که بیشترین هماهنگی بین نخ ها باشه و یه هسته ی منطقی ، منتظر هسته ی منطقیِ دیگری نشه) و شرکت های بزرگ دنیا هم براشون بهینه کردن کدها ، آسون نیست .
واسه ی همینه که هر بار در آپدیت برنامه (های بزرگ) میبینیم که مینویسن کدهامون را بهینه تر کردیم (یک عاملش) .
که البته در این باره (هماهنگی بین نخ ها) ، در پست بعدی میپردازیم .
Bookmarks