سرعت و بازدهی کدهای مختلف 1
سلام
این مسئله بسیار مهم است ولی واقعاً یک پست برای آن کافی نیست و به همین دلیل هم اصلاً نمیخواستم وارد این بحث شوم...
ولی با پیشنهاد یکی از دوستان و توجه به این نکته که تعداد زیادی از افراد ا سرعت و بازدهی کدهای خودشان در XNA مشکل دارند، تصمیم گرفتم هر چند ناقص حداقل یک پست را به این مطلب اختصاص دهم.
اول لازم میدانم متذکر شوم که رعایت اصول بازدهی و سرعت در برنامه های سه بعدی بسیار بسیار مهم تر از هر محیط دیگری است، چون برنامه در یک حلقه بینهایت با یک تعداد مشخص فریم در ثانیه (مثلاً حدود 60 فریم در ثانیه) تمام عملیات های لازم را مجدداً انجام میدهد و با توجه به تعداد اشیای در صحنه و تعداد مثلث های مصرف شده و... خود برنامه شما و نیز هسته DirectX و کارت گرافیکی باید حجم زیادی از اعمال را انجام دهد و کوچکترین بیدقتی پردازشی میتواند تاثیرات مخربی در سرعت و بازدهی عمومی برنامه بگذارد.
نکته بعدی آنجا است که شما همواره هم باید مراقب کد نویسی مستقیم خودتان باشد و هم کدنویسی داخلی ناپیدا توابع آماده.
در صورت رعایت صحیح نکات و کدنویسی خوب با XNA میتواند به حدود 70 درصد سرعت برنامه های سه بعدی VC++,DirectX برسید که سرعت و بازدهی مناسبی است به سادگی و سرعت تولید محصول XNA و زبانهای VB.Net و C#.Net در مقابل ++VC و DirectX می صرفه!
اصلی ترین موارد کاهش سرعت و بازدهی به ترتیب چنین هستند:
==============
----- رابطه خصاصت و تنبلی و تعریف متغییر !
=====
اصولا مسئله این است که کار باید انجام شود و کسی نمیگوید که دستور باید حذف شود ولی باید از تکرار عملیتهای موازی و مشابه جلوگیری کرد و حاصل انجام یک عملیت را بارها مورد استفاده قرار داد و مجدد بار تولید ان نتیجه را به برنامه تحمیل نکرد.
ساده ترین کدهای خصاصت میتواند چیزهایی شبیه این باشد ...
کد:
کد:
کد اشتباه
//C#.Net
a = x * y + z / 2 * w + 25 + f;
b = x * y + z / 2 * w + 13 + e;
//VB.Net
a = x * y + z / 2 * w + 25 + f
b = x * y + z / 2 * w + 13 + e
کد:
کد:
کد صحیح
//C#.Net
t = x * y + z / 2 * w;
a = t + 25 + f;
b = t + 13 + e;
//VB.Net
t = x * y + z / 2 * w
a = t + 25 + f
b = t + 13 + e
=====
موارد بدتر و وحشتناک تر میتواند چنین باشد:
کد:
کد:
کد اشتباه
//C#.Net
a = Function1(collection["ItemName"]);
b = Function2(collection["ItemName"]);
//VB.Net
a = Function1(collection("ItemName"))
b = Function2(collection("ItemName"))
کد:
کد:
کد صحیح
//C#.Net
t = collection["ItemName"];
a = Function1(t);
b = Function2(t);
//VB.Net
t = collection("ItemName")
a = Function1(t)
b = Function2(t)
دقت کنید که ارجاع به یک آیم مجموعه یک خط دستور ساده نیست ...
مواردی کلید String و بیشتر تایپ های دیگر را دارند در ذات دو حلقله for تو در توی سنگین را به برنامه شما تحمیل میکنند ...
حلقه اول تک تک عناصر مجموعه کلید را با کلید داده شده شما چک میکند تا عنصر دلخواه شما را پیدا کند و حلقه دوم که کمتر به آن دقت و توجه میشود حلقه ای است که تک تک کاراکترهای دو String را با هم مقایسه میکند تا بفهمد دو String برابر هستند یا خیر ...
در واقع دسترسی به عناصر یک مجموعه معمولاً یک اسکن حجیم را به سیستم تحمیل میکند تا عنصر مورد نظر پیدا شود.
در مواردی که این مجموعه ثابت است بهتر است اشاره گر عنصر مربوطه فقط و فقط یک بار در برنامه به دست آورده و در متغییری محلی در سطح کلاس ذخیره کنید.
کد:
کد:
کد اشتباه
//C#.Net
public class Class1
: XNA.Game
{
private XNA.Graphics.Effect m_Effect;
protected override void LoadContent()
{
var content = this.Content;
this.m_Effect = content.Load<XNA.Graphics.Effect>("...");
}
protected override void Update(XNA.GameTime gameTime)
{
value = ...;
this.m_Effect.Parameters["Name"].SetValue(value);
}
//VB.Net
Public Class Class1
Private m_Effect As XNA.Graphics.Effect
Protected Overrides Sub LoadContent()
Dim content = Me.Content
Me.m_Effect = content.Load(Of XNA.Graphics.Effect)("...")
End Sub
Protected Overrides Sub Update(ByVal gameTime As XNA.GameTime)
value = ...
Me.m_Effect.Parameters("Name").SetValue(value)
End Sub
کد:
کد:
کد صحیح
//C#.Net
public class Class1
: XNA.Game
{
private XNA.Graphics.Effect m_Effect;
private XNA.Graphics.EffectParameter m_Effect_Name;
protected override void LoadContent()
{
var content = this.Content;
this.m_Effect = content.Load<XNA.Graphics.Effect>("...");
this.m_Effect_Name = this.m_Effect.Parameters["Name"];
}
protected override void Update(XNA.GameTime gameTime)
{
value = ...;
this.m_Effect_Name.SetValue(value);
}
//VB.Net
Public Class Class1
Private m_Effect As XNA.Graphics.Effect
Private m_Effect_Name As XNA.Graphics.EffectParameter
Protected Overrides Sub LoadContent()
Dim content = Me.Content
Me.m_Effect = content.Load(Of XNA.Graphics.Effect)("...")
Me.m_Effect_Name = Me.m_Effect.Parameters("Name")
End Sub
Protected Overrides Sub Update(ByVal gameTime As XNA.GameTime)
value = ...
Me.m_Effect_Name.SetValue(value)
End Sub
=====
نوع دیگری از مسئله فوق خود را در حلقه های for و while و... آشکار و پنهان نشان میدهد ...
دستوری که در داخل حلقه نوشته میشود یک خط نیست، به معنای انکه شاید به نظر شما یک خط باشد ولی اگر حلقه 100 بار تکرار شود آن یک خط شما 100 بار اجرا میشود و معادل 100 خط کدنویسی فشار به برنامه تحمیل میکند.
حلقه های آشکار for و while هستند که ما داریم و حلقه های پنهان جایی هستند که ما نمیبینیم مثل حلقه for ای که یک مجموعه را برای پیدا کردن عنصری جستجو میکند یا حلقه while که Game.Update کد قبلی را مدام و تا انتهای برنامه اجرا میکند.
شما اگر یک عملیات و یک خط کد را از داخل حلقه ای کم کنید و در عوضش مجبور شویبد 10 یا 20 خط کد خارج آن حلقه اضافه کنید (تا آن یک خط داخل حلقه کم شود) باز هم سود کرده اید !!!
چون یک خط داخل حلقه با 100 بار معادل 100 خط کد نویسی را به برنامه تحمیل میکند و در حالی 10 یا 20 خط خارج حلقه همان 10 یا 20 خط است.
مثال قبلی هم در مبحث گفتاری الآن میگنجد ...
در مثال قبلی هم ما با یک تعریف متغییر کد جستجوی یک مجموعه را از متد مادام الاجرای Game.Update به متد یکبار اجرا شوند Game.LoadContent منتقل کردیم و جالب آنکه دیگر بحث حلقه 100 یا 200 دوری نیست! متد Game.Update نزدیک 60 بار فقط در هر یک ثانیه میتواند اجرا شود (کری به دقیقهع و ساعت نداریم) و یک عملیات کمتر ما نتیجه 60 عملیات کمتر در هر ثانیه را بر عهده دارد.
(بگذریم که خود جستجو در یک مجموعه برای یافتن یک عنصر هم همانطور که قبلاً بیان شد یک عملیات ساده نیست و خودش دو تا حلقه تو در تو است!!!)