بهینهسازی مصرف حافظه با ویژگیهای مدرن داتنت
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۲/۱۴ ۰۷:۵۳
آدرس: www.dntips.ir
List<T>) و دیکشنریها (Dictionary<TKey, TValue>) میپردازد و نشان میدهد که چگونه استفاده صحیح از این ویژگیها میتواند تأثیر چشمگیری بر عملکرد و میزان حافظه مصرفی برنامههای داتنت داشته باشد.List<T> و Dictionary<TKey, TValue>, برای ذخیره دادهها، از آرایههایی با اندازه ثابت در پشت صحنه استفاده میکنند. هنگامیکه تعداد عناصر، از ظرفیت آرایه فعلی بیشتر میشود، یک آرایه جدید با ظرفیت بزرگتر تخصیص داده شده و عناصر موجود، به آرایه جدید کپی میشوند. اگرچه کامپایلر و زمان اجرا (Runtime) در بیشتر موارد این فرآیند را به صورت بهینه مدیریت میکنند، اما یک سناریوی قابل توجه وجود دارد که در آن مداخله توسعهدهنده میتواند بهبود چشمگیری ایجاد کند.[Benchmark]
public Dictionary<string, string> DefaultCapacity()
{
return new Dictionary<string, string>
{
["1"] = "1",
["2"] = "2",
["3"] = "3",
["4"] = "4",
["5"] = "5",
["6"] = "6",
["7"] = "7",
["8"] = "8",
};
}
[Benchmark]
public Dictionary<string, string> ExactCapacity()
{
return new Dictionary<string, string>(8)
{
["1"] = "1",
["2"] = "2",
["3"] = "3",
["4"] = "4",
["5"] = "5",
["6"] = "6",
["7"] = "7",
["8"] = "8",
};
}DefaultCapacity (ظرفیت پیشفرض) به طور قابل توجهی بیشتر از حالت ExactCapacity (ظرفیت دقیق) است:| Method | Mean | Error | StdDev | Gen0 | Allocated |
| DefaultCapacity | 113.00 ns | 0.376 ns | 0.333 ns | 0.1185 | 992 B |
| ExactCapacity | 65.57 ns | 0.623 ns | 0.521 ns | 0.0526 | 440 B |
Dictionary<string, string> فراخوانی میشود که مجموعه را با ظرفیت صفر مقداردهی اولیه میکند. سپس، عناصر به صورت تکی به مجموعه اضافه میشوند و در صورت نیاز، الگوریتم تغییر اندازه داخلی (Internal Resizing Algorithm) فعال میشود. برای درک بهتر این موضوع، به مثال زیر توجه کنید:var d = new Dictionary<int, int>();
Console.WriteLine($"Capacity: {d.Capacity,2}, Count: {d.Count}");
for (int i = 0; i < 8; ++i)
{
d.Add(i, i);
Console.WriteLine($"Capacity: {d.Capacity,2}, Count: {d.Count}");
}List<T>), علاوه بر تعیین ظرفیت اولیه از طریق سازنده، یک روش کارآمدتر برای مقداردهی اولیه وجود دارد: عبارات مجموعه (Collection Expressions). عبارات مجموعه نه تنها یک ویژگی ظاهری برای سادهتر کردن نحوه مقداردهی اولیه مجموعهها هستند، بلکه از نظر عملکردی نیز بهینهتر عمل میکنند. برخلاف مقداردهندههای مجموعه، عبارات مجموعه ظرفیت دقیق مورد نیاز را تعیین میکنند و به همین دلیل سرعت اجرای بالاتری دارند:[Benchmark]
public List<string> InitializerDefaultCapacity()
{
return new List<string> { "1", "2", "3", "4", "5", "6", "7", "8" };
}
[Benchmark]
public List<string> InitializerExactCapacity()
{
return new List<string>(8) { "1", "2", "3", "4", "5", "6", "7", "8" };
}
[Benchmark]
public List<string> CollectionExpression()
{
return ["1", "2", "3", "4", "5", "6", "7", "8"];
}| Method | Mean | Error | StdDev | Gen0 | Allocated |
| InitializerDefaultCapacity | 57.46 ns | 0.244 ns | 0.228 ns | 0.0440 | 368 B |
| InitializerExactCapacity | 26.59 ns | 0.109 ns | 0.102 ns | 0.0162 | 136 B |
| CollectionExpression | 10.69 ns | 0.012 ns | 0.012 ns | 0.0163 | 136 B |
["1", "2", "3", "4", "5", "6", "7", "8"]) در مقایسه با هر دو روش مقداردهی اولیه با استفاده از سازنده و مقداردهنده مجموعه، هم از نظر سرعت اجرا و هم از نظر میزان حافظه تخصیص یافته، عملکرد بهتری دارد. این به این دلیل است که عبارات مجموعه به طور مستقیم ظرفیت نهایی لیست را تعیین میکنند و از تخصیصهای میانی غیرضروری جلوگیری میکنند.| ویژگی | مقداردهنده مجموعه (ظرفیت پیشفرض) | مقداردهنده مجموعه (ظرفیت دقیق) | عبارت مجموعه (Collection Expression) |
| تعیین ظرفیت اولیه | خیر | بله (دستی) | بله (خودکار) |
| تعداد تخصیص حافظه | چندگانه | یک بار | یک بار |
| سرعت اجرا | کندتر | سریعتر | سریعترین |
| میزان حافظه تخصیصیافته | بیشتر | کمتر | کمتر |
| سهولت استفاده | آسان | متوسط | آسان |