از طرف خودم، فقط میخواهم اندازهگیریها و نتایج خود را به شما ارائه دهم؛ حلقههای معکوس را برای دوستداران «سبک C++» حلقههای نوشتن به آنها اضافه کردم.
کد:
public class Bench
{
private List<int> _list;
private int[] _array;
[Params(100000, 10000000)] public int N;
[GlobalSetup]
public void Setup()
{
const int MIN = 1;
const int MAX = 10;
Random random = new Random();
_list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
_array = _list.ToArray();
}
[Benchmark]
public int ForList()
{
int total = 0;
for (int i = 0; i < _list.Count; i++)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForListFromEnd()
{
int total = 0;t
for (int i = _list.Count-1; i > 0; i--)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForeachList()
{
int total = 0;
foreach (int i in _list)
{
total += i;
}
return total;
}
[Benchmark]
public int ForeachArray()
{
int total = 0;
foreach (int i in _array)
{
total += i;
}
return total;
}
[Benchmark]
public int ForArray()
{
int total = 0;
for (int i = 0; i < _array.Length; i++)
{
total += _array[i];
}
return total;
}
[Benchmark]
public int ForArrayFromEnd()
{
int total = 0;
for (int i = _array.Length-1; i > 0; i--)
{
total += _array[i];
}
return total;
}
}
سرعت عملکرد در NET Core 2.2 و 3.0 تقریباً یکسان است. در اینجا چیزی است که من توانستم در NET Core 3.0 دریافت کنم:
میتوان نتیجه گرفت که پردازش تکراری یک مجموعه آرایه به دلیل بهینهسازیهای داخلی و تخصیص صریح اندازه مجموعه سریعتر است. همچنین لازم به یادآوری است که مجموعه List مزایای خاص خود را دارد و شما باید بسته به محاسبات مورد نیاز از مجموعه مناسب استفاده کنید. حتی اگر برای کار با حلقه ها منطق می نویسید، فراموش نکنید که این یک حلقه معمولی است و همچنین مشمول بهینه سازی حلقه ممکن است. مدت ها پیش مقاله ای در habr منتشر شد: https://habr.com/ru/post/124910/. هنوز هم مرتبط است و خواندن آن توصیه می شود.
پرتاب
یک سال پیش، من در یک شرکت بر روی یک پروژه قدیمی کار کردم، در آن پروژه، پردازش اعتبار میدانی از طریق ساختار try-catch-throw عادی بود. من قبلاً فهمیدم که این منطق تجاری ناسالم برای پروژه است، بنابراین هر زمان که ممکن بود سعی کردم از چنین طرحی استفاده نکنم. اما بیایید بفهمیم که چرا رویکرد رسیدگی به خطاها با چنین ساختاری بد است. من یک کد کوچک برای مقایسه دو رویکرد نوشتم و برای هر گزینه معیارهایی ایجاد کردم.
کد:
public bool ContainsHash()
{
bool result = false;
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
return result;
}
public bool ContainsHashTryCatch()
{
bool result = false;
try
{
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
if(!result)
throw new Exception("false");
}
catch (Exception e)
{
result = false;
}
return result;
}
نتایج در NET Core 3.0 و Core 2.2 نتایج مشابهی دارند (NET Core 3.0):
Try catch درک کد را سختتر میکند و زمان اجرای برنامه شما را افزایش میدهد. اما اگر به این ساختار نیاز دارید، نباید آن خطوط کدی را وارد کنید که انتظار نمی رود خطاها را مدیریت کنند - این کار درک کد را آسان تر می کند. در واقع، مدیریت استثناها نیست که سیستم را بارگذاری می کند، بلکه پرتاب خطاها از طریق ساختار استثنایی جدید است.
پرتاب استثناها کندتر از کلاس هایی است که خطا را در قالب مورد نیاز جمع آوری می کند. اگر در حال پردازش یک فرم یا برخی از داده ها هستید و به وضوح می دانید که خطا باید چه باشد، چرا آن را پردازش نمی کنید؟
اگر این وضعیت استثنایی نیست، نباید یک ساختار () Exception جدید بنویسید. دست زدن و پرتاب استثنا خیلی گران است!!!
در طول 5 سال تجربه کار بر روی پلتفرم دات نت، با پروژه های زیادی برخورد کردم که از تطبیق رشته ها استفاده می کردند. من همچنین تصویر زیر را دیدم: یک راه حل Enterprise با پروژه های زیادی وجود داشت که هر کدام از آنها مقایسه رشته ها را متفاوت انجام می داد. اما از چه چیزی باید استفاده کرد و چگونه آن را یکسان کرد؟ در کتاب CLR via C# توسط Richter اطلاعاتی خواندم که متد ToUpperInvariant() سریعتر از ToLowerInvariant() است.
گزیده ای از کتاب:
البته، من آن را باور نکردم و تصمیم گرفتم چند آزمایش را روی دات نت فریم ورک اجرا کنم و نتیجه آن مرا شوکه کرد - بیش از 15 درصد افزایش عملکرد. سپس، صبح روز بعد، پس از رسیدن به محل کار، این اندازهها را به مافوق خود نشان دادم و به آنها اجازه دادم به کد منبع دسترسی داشته باشند. پس از این، 2 پروژه از 14 پروژه برای تطبیق با اندازه گیری های جدید تغییر یافت و با توجه به اینکه این دو پروژه برای پردازش جداول بزرگ اکسل وجود داشتند، نتیجه برای محصول بیش از حد قابل توجه بود.
همچنین اندازه گیری های نسخه های مختلف NET Core را به شما ارائه می دهم تا هر یک از شما بتوانید به سمت بهینه ترین راه حل انتخاب کنید. و من فقط می خواهم اضافه کنم که در شرکتی که من در آن کار می کنم، از ToUpper() برای مقایسه رشته ها استفاده می کنیم.
کد:
public const string defaultString = "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";
[Benchmark]
public bool ToLower()
{
return defaultString.ToLower() == defaultString.ToLower();
}
[Benchmark]
public bool ToLowerInvariant()
{
return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
}
[Benchmark]
public bool ToUpper()
{
return defaultString.ToUpper() == defaultString.ToUpper();
}
[Benchmark]
public bool ToUpperInvariant()
{
return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
}
در NET Core 3.0، افزایش برای هر یک از این روش ها ~x2 است و بین پیاده سازی ها تعادل ایجاد می کند.
تدوین ردیف
در آخرین مقاله خود که این عملکرد را به طور خلاصه شرح دادم، می خواهم کلمات خود را اصلاح و تکمیل کنم. کامپایل چند سطحی زمان راهاندازی راهحل شما را سرعت میبخشد، اما شما قربانی این میکنید که بخشهایی از کد شما در نسخهای بهینهتر در پسزمینه کامپایل شود، که میتواند سربار کوچکی را ایجاد کند. با ظهور NET Core 3.0، زمان ساخت پروژه هایی با فعال بودن کامپایل لایه کاهش یافته و باگ های مرتبط با این فناوری برطرف شده است. پیش از این، این فناوری منجر به خطاهایی در اولین درخواستها در ASP.NET Core میشد و در اولین ساخت در حالت کامپایل چند سطحی ثابت میشد. در حال حاضر به طور پیش فرض در NET Core 3.0 فعال است، اما در صورت تمایل می توانید آن را غیرفعال کنید. اگر در موقعیت رهبر تیم، ارشد، میانی یا رئیس یک بخش هستید، باید بدانید که توسعه سریع پروژه ارزش تیم را افزایش می دهد و این فناوری به شما امکان می دهد در زمان برای هر دو توسعه دهنده صرفه جویی کنید. و زمان خود پروژه
ارتقا سطح دات نت
نسخه NET Framework / NET Core خود را ارتقا دهید. اغلب، هر نسخه جدید دستاوردهای عملکرد بیشتری را ارائه می دهد و ویژگی های جدیدی را اضافه می کند.
اما دقیقاً چه فوایدی دارد؟ بیایید به برخی از آنها نگاه کنیم:
NET Core 3.0 تصاویر R2R را معرفی کرد که زمان راه اندازی برنامه های NET Core را کاهش می دهد.
با نسخه 2.2، Tier Compilation ظاهر شد که به لطف آن برنامه نویسان زمان کمتری را برای راه اندازی یک پروژه صرف می کنند.
پشتیبانی از استانداردهای جدید دات نت.
پشتیبانی از نسخه جدید زبان برنامه نویسی
بهینه سازی، با هر نسخه جدید، بهینه سازی کتابخانه های پایه Collection/Struct/Stream/String/Regex و موارد دیگر بهبود می یابد. اگر از .NET Framework به .NET Core مهاجرت می کنید، عملکرد قابل توجهی را افزایش خواهید داد. به عنوان مثال، من پیوندی را به برخی از بهینه سازی هایی که به .NET Core 3.0 اضافه شده اند پیوست می کنم: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/
نتیجه
هنگام نوشتن کد، ارزش آن را دارد که به جنبه های مختلف پروژه خود توجه کنید و از ویژگی های زبان برنامه نویسی و پلتفرم خود برای رسیدن به بهترین نتیجه استفاده کنید. خوشحال می شوم دانش خود را در رابطه با بهینه سازی در دات نت به اشتراک بگذارید.