اشتراکها
اشتراکها
بهینه سازی عملکرد EF
اشتراکها
تفاوت کارآیی WPF در دات نت 4 و 4.5
هدف از توابع خطی(Inline)
استفاده از توابع، مقداری بر زمان اجرای برنامه میافزاید؛ هرچند که این زمان بسیار کم و در حد میلی ثانیه است، اما باری را بر روی برنامه قرار میدهد و علت این تاخیر زمانی این است که در فراخوانی و اعلان توابع، کامپایلر یک کپی از تابع مورد نظر را در حافظه قرار میدهد و در فراخوانی تابع، به آدرس مذکور مراجعه میکند و در عین حال آدرس موقعیت توقف دستورات در تابع main را نیز ذخیره میکند تا پس از پایان تابع، به آدرس قبل برگردد و ادامهی دستورات را اجرا کند. در نتیجه این آدرس دهیها و نقل و انتقالات بین آنها بار زمانی را در برنامه ایجاد میکند که در صورت زیاد بودن توابع در برنامه و تعداد فراخوانیهای لازم، زمان قابل توجهی خواهد شد.
یکی از تکنیکهای بهینه که برای کاهش زمان اجرای برنامه توسط کامپایلرها استفاده میشود استفاده از توابع خطی (inline) است. این امکان در زبان C با عنوان توابع ماکرو(Macro function) و در ++C با عنوان توابع خطی (inline function) وجود دارد.
در واقع توابع خطی به کامپایلر پیشنهاد میدهند، زمانی که سربار فراخوانی تابع بیشتر از سربار بدنه خود متد باشد، برای کاهش هزینه و زمان اجرای برنامه از تابع به صورت خطی استفاده کند و یک کپی از بندهی تابع را در قسمتی که تابع ما فراخوانی شده است، قرار دهد که مورد آدرس دهی از میان خواهد رفت!
نمونه ای از پیاده سازی این تکنیک در زبان ++C :
inline type name(parameters) { ... }
بررسی متدهای خطی در سی شارپ
به مثال زیر توجه کنید:
قسمتهای getter و setter مربوط به پراپرتیها سربار اضافی بر کلاس Vector میافزایند. این موضوع شاید آنچنان مسئلهی مهمی نباشد. ولی فرض کنید این پراپرتیها به شکل زیر داخل حلقهای طولانی قرار گیرند. اگر با استفاده از یک پروفایلر زمان اجرای برنامه را زیر نظر بگیرید، خواهید دید که بیش از 90 درصد آن صرف فراخوانیهای متدهای بخشهای get , set پراپرتیها است. برای این منظور باید مطمئن شویم که فراخوانی این متدها، به صورت خطی صورت میگیرد!
public class Vector { public double X { get; set; } public double Y { get; set; } public double Z { get; set; } // ... }
برای این منظور آزمایشی را انجام میدهیم. فرض کنید کلاسی را به شکل زیر داشته باشیم:
public class MyClass { public int A { get; set; } public int C; }
static void Main() { MyClass target = new MyClass(); int a = target.A; Console.WriteLine("A = {0}", a); int c = target.C; Console.WriteLine("C = {0}", c); }
int a = target.A; 0000003e mov ecx,edi 00000040 cmp dword ptr [ecx],ecx 00000042 call dword ptr ds:[05FA29A8h] 00000048 mov esi,eax 0000004a mov dword ptr [esp+4],esi int c = target.C; 00000098 mov edi,dword ptr [edi+4] MyClass.get_A() looks like this: 00000000 push esi 00000001 mov esi,ecx 00000003 cmp dword ptr ds:[03B701DCh],0 0000000a je 00000011 0000000c call 76BA6BA7 00000011 mov eax,dword ptr [esi+0Ch] 00000014 pop esi 00000015 ret
چه اتفاقی افتاده است؟
کامپایلر سی شارپ در زمان کامپایل، کدهای برنامه را به کدهای IL تبدیل میکند و JITکامپایلر، این کدهای IL را گرفته و کد سادهی ماشین را تولید میکند. لذا به دلیل اینکه JIT با معماری پردازنده آشنایی کافی دارد، مسئولیت تصمیم گیری اینکه کدام متد به صورت خطی فراخوانی شود برعهدهی آن است. در واقع این JIT است که تشخیص میدهد که آیا فراخونی متد به صورت خطی مناسب است یا نه و به صورت یک معاوضه کار بین خط لوله دستورالعملها و کش است.
اگر شما برنامهی خود را با (F5) و همگام با دیباگ اجرا کنید، تمام بهینه سازیهای JIT که Inline Method هم یکی از آنهاست، از کار خواهند افتاد. برای مشاهدهی کد بهینه شده باید با بدون دیباگ (CTRL+F5) برنامه خود را اجرا کنید که در آن صورت مشاهده خواهید کرد، متد getter مربوط به پراپرتی A به صورت خطی استفاده شده است.
int a = target.A; 00000024 mov ebx,dword ptr [edi+0Ch]
JIT محدودیت هایی برای فراخونی به صورت خطی متدها دارد :
- متد هایی که حجم کد IL آنها بیشتر از 32 بایت است.
- متدهای بازگشتی.
- متدهایی که با اتریبیوت MethodImpl علامتگذاری شدند و MethodImplOptions.NoInlining اعمال شده بر آن
- متدهای virtual
- متدهایی که دارای کد مدیریت خطا هستند
- Methods that take a large value type as a parameter
- Methods with complicated flowgraphs
برای اینکه در سی شارپ به کامپایلر اعلام کنیم تا متد مورد نظر به صورت خطی مورد استفاده قرار گیرد، در دات نت 4.5 توسط اتریبیوت MethodImpl و اعمال MethodImplOptions.AggressiveInlining که یک نوع شمارشی است میتوان این کار را انجام داد. مثال:
using System; using System.Diagnostics; using System.Runtime.CompilerServices; class Program { const int _max = 10000000; static void Main() { // ... Compile the methods. Method1(); Method2(); int sum = 0; var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { sum += Method1(); } s1.Stop(); var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { sum += Method2(); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.Read(); } static int Method1() { // ... No inlining suggestion. return "one".Length + "two".Length + "three".Length + "four".Length + "five".Length + "six".Length + "seven".Length + "eight".Length + "nine".Length + "ten".Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Method2() { // ... Aggressive inlining. return "one".Length + "two".Length + "three".Length + "four".Length + "five".Length + "six".Length + "seven".Length + "eight".Length + "nine".Length + "ten".Length; } } Output 7.34 ns No options 0.32 ns MethodImplOptions.AggressiveInlining
مطالعه بیشتر:
نظرات مطالب
NHibernate 3.2
de4dot is an open source (GPLv3) .NET deobfuscator and unpacker written in C#. It will try its best to restore a packed and obfuscated assembly to almost the original assembly. Most of the obfuscation can be completely restored (eg. string encryption), but symbol renaming is impossible to restore since the original names aren't (usually) part of the obfuscated assembly.
Supported obfuscators/packers
- Agile.NET (aka CliSecure)
- Babel.NET
- CodeFort
- CodeVeil
- CodeWall
- CryptoObfuscator
- DeepSea Obfuscator
- Dotfuscator
- .NET Reactor
- Eazfuscator.NET
- Goliath.NET
- ILProtector
- MaxtoCode
- MPRESS
- Rummage
- Skater.NET
- SmartAssembly
- Spices.Net
- Xenocode