











مدیریت مفید حافظه در c++ و Memory Pool
تألیف : امیر نوری
دانلود متن : http://depositfiles.com/en/files/5z7xzobsd
زبان c++ یک زبان برنامه نویسیه قویه خوب چرا قویه؟ شروع میکنی و یک برنامه ساده باهاش مینویسی و خوشحال میشی! ولی وقتی میخوایی یه بازی بنویسی هیجان زده میشی بدش میری یه بازیه اسنیک مینویسی خوشحال تر میشی بعد از چند وقت بر فرض اینکه مثل خیلی های دیگه آموزش ندیده باشی تصمیم میگیری با دوستات یه بازی بزرگ تر بنویسی خوب شروع میکنی که بنویسی هنوز به وسطش نرسیدی میبینی ای بابا! چقدر سرعت اجرای برنامت پایینه! چقدر اشکال زدایش سخته(راستش تا حالا از این اصطلاحات استفاده نکردم همونdebug)اصلا معلوم نیست چی به چیه حالا شروع میکنی هم به مهارتت هم به c++ لعنت میفرستی...
خوب این یه سناریویه تقریبا واقعه ای بود تو این آموزش میخوام یه جنبه کوچیک از برنامه نویسی که تو بازی خیلی کار بردی و مهمه را توضیح بدم که مربوط به مدیریت حافظه و بازده برنامه میشه نه bug-free بودنش.من خودم برنامه نویسی خیلی با تجربه ای نیستم ولی چیزایی را که درسته میگم و از درستیشون اطمینان دارم البته اگر اشکالی بود حتما بگید!
Memory Pool
والا نمیدونم به فارسی چی میشه چون برنامه نویسی را من آکادمیک یاد نگرفتم و خودم یاد گرفتم تاحالا از کسی اینو نپرسیدم! به هر حال memory pool یک بخش حافظه از پیش اختصاص داده شدست که آماده کردیم و میخواییم در ادامه برنامه استفاده کنیم خوب این یعنی چی و چرا خوبه را در ادامه میگیم.
وقتی شما دارید برنامه نویسی میکنید متغییر ها کلاس ها و هرچی که دارید در نوع حافظه قرار میگیرند(2 سبک ذخیره شدن)1. stack 2.heap
1.stack
حافظه stack یک حافظه کوتاه مدت در برنامه است یا محلی یعنی مثلا داخل یک تابع تعرف میکنی
int a;
این میره تو حافظه stack ذخیره میشه و وقتی برنامه از تابع خارج میشه اینم از بین میره یعنی مثلا اگر متغیر a 4 بایت حافظه بگیره وقتی از تابع خارج بشه اون 4 بایت باز به فضا ی برنامه اختصاص داده میشه (یه سرچ راجبه virtual memory و physical memory بکنید)stack را میشه مثل شکل زیر تصور کرد و هر کدوم از اون مربع ها را میگیم یک stack frame که هرکدومش مال یک تابع میشه همونطور که میبینید اولی ما تابع main() هست و بعدی هم برای تابع فرضیه printaint() که داخلش یک متغیر اختصاص داده شده وقتی تابع فراخونده میشه حافظه اختصاص داده میشه و وقتی هم از تابع برنامه رد میشه اون حافظه را برمیگردونه.

خوب همینقدر توضیح بسه به نظرم یه سری چیزایه دیگه میشه گفت که راحت میتونید سرچ کنید بخویند آسونه!.
2.Heap
و حالا اصل کار! خوبیه stack اینه که خیلی راحت توش میشه حافظه داد و ستد کرد و بازده ی خوبی داره چو ن یه چیزی وارد میشه بعد خارج میشه یعنیfirst in first out و به ترتیب ولی اوضاع داخل heap یه خورده بهم ریخته است.وقتی ما میخوایم یک شی را به صورت دایم تعریم کنیم یا آزاد از محدوده ی دیدش (scope)داخل برنامه باید حافظه را داخل heap به اون اختصاص بدیم که همون عملگر های new و delete یا malloc و free باهاش کار میکنند.
همونطور که گفتم اوضاع داخل heap خیلی خرابه! وقتی شما شروع به دادن حافظه داخل heap میکنید به اون یک block از حافظه در heap اختصاص داده میشه و وقتی چیز دیگه را شما میسازید سیستم عامل میاد داخل heap را میگرده و یک فضای خالی براش پیدا میکنه و بهش میده و حالا وقتی شما فضا را با مثلا delete آزاد میکنید اون block سر جاش به همون اندازه میمونه فقط داخلش آزاد میشه یعنی دفعه ی بعد چیزی بزرگ تر از مقدار قبلیش داخلش جا نمیشه اگه مقدار بیشتر نیاز باشده heap اونو ول میکنه میره یکی بزرگ تر میسازه ولی اگه یک مقدار کوچک تر را بخوایید اختصاص بدید همین block باز تقسیم میشه و...
حالا تو شکل ببینید اولی از سمت راست یک heap را نشون میده که فقط یک بار فضا اختصاص داده شده و بعدی پس از چند ده تا new و delete

میبینید که چه ریختی میشه! تازه این خوبشه از این خیلی بدتره! ولی حالا این کجاش بده ؟ همه جاش! وقتی شما از شروع برنامتون کلی میگذره این heap زبون بسته کلا تیکه تیکه میشه و 2 تا اتفاق بد میفته اولیش که همیشه میفته دومیش یکدفعه دیدی شد! که بعد عمرا نمیتونی کاریش کنی!
اولی اینه که هر بار میخواد حافظه اختصاص بده سیستم عامل به برنامتون باید بره تو heap را بگرده که حالا کی جای خالی پیدا کنه مناسبه کار شما
و دوم اینکه یه دفه دیدی پیدا نکرد!! یعنی heap شما حالا تیکه تیکه شده ولی تیکه ها خیلی کوچیک تر از اونه که چیزی که الان میخوایی جا بدی جا بشه الانه که برنامه میترکه و تو هم کل عمرتو میزاری debug کنی باز مییبنی ای بابا یه بار سر 10 دقیقه منفجر میشه یه بار بعد از نیم ساعت .. باور کن اصلا خوب نیست!
اینجا که میفهمی ای بابا مثل اینکه باید بیخیالش بشی ! ولی راه حل این موضوع چیه؟! درست فهمیدی
memory pool!
همونطور که گفتم Memory pool یه قطعه از حافظست که اولا بسته به نیاز باید به اندازه کافی بزرگ باشه که دیگه داخل اون فعالیت های شلوغتو انجام میدی مثلا اگه تو بازیت یا برنامت در هر فریم میخوایی چند صد بار یه چیزی را new و delete کنی این بهت خیلی خیلی کمک میکنه هم برنامت از بین نمیره هم سرعتش افزایش پیدا میکنه(البته مدیریت حافظه اصلا داخل memory pool تموم نمیشه باید یک heap manager هم بنویس که باشه بعدا!وشاید چیزای دیگه)
خوب حالا memory pool را تعریف کردیم بعدش چه کنیم ؟ بعدش قبل از اینکه بخواد تیکه تیکه بشه خودت تیکه تیکش میکنی! به تیکه های مساوی به صورت یک لینک لیست اینطوری که هر بار یک فضا میخوای آدرس یک خونه خالی داده میشه و فضای جدید ساخته نمیشه!اشکال را نگاه کن:
شکل اول یک memory pool تیکه تیکه شده را نشون میده فعلا همین! شکل دوم تیکه هارا به هم متصل کردیم اینطوری که هرکدوم به بعدیش اشاره میکنه(متاسفانه دیگه خیلی جزیات را بگم مثل اشاره گراش و چطوری بودنش! ) شکل سوم حالات memory pool را بعد از چند بار استفاده نشون میده

میبینی چطوریه!؟ مثلا یه بار تو حافظه نیاز داری خوب خونه اول را بهت میده و هر بار هم که فضا را آزاد میکنی اون فضای که آزاد کردی را میاره اول لیست میشه مثل شکل 3 .وقتی memory pool جا کم آورد راحت در ادامشه یه فضای جدید اضافه میکنی حالا کو تا کم بیاره!
تا اینجا چند تا از جادوهاشو دیدی اولا اینکه نیاز نیست کل heap را بگردی و کلی کار اضافی برای اختصاص حافظه بلکه سری برنامه میره این تو یکی برا خودش پیدا میکنه خیلی سریع!و مدیریتش هم به مراتب باحال تره!
ولی خوب یه جورایی هم هست که اصلا بد هم نیست ولی محدودیته. و اونم اندازه block هاست چون باید ثابت باشن مثلا تو میتونی یک memory pool بسازی برا گلوله هات تو بازی خوب اندازه گلوله ها ثابتند پس مشکلی نیست ولی بعضی موقع ها هم یه سری بهم ریخته گی ایجاد میشه که اصلا مهم نیست و قابل مدیریت 100% و خیلی هم راحته حالا میدونی memory pool چیه و سوال اینه چطوری پیاده سازیش کنیم؟خوب اینطوری
برای پیاده سازیش من از یک مقاله داخل code project کمک میگیرم چون کدشم خوب و تمیزه البته memory pool اون یه سری چیز خیلی کوچیک کم داره که خودت میتونه بعدا درست کنی از چیزی که من تاحالا برات گفتم فراتره و بهتر.
اول لینک: http://www.codeproject.com/KB/cpp/MemoryPool.aspx
این ساختار برنامست الان یه نگاه کن بعدش هرچی میخوندی باز برگرد بالا باز نگاش کن.

حالا تو اینجا آمده یک struct ساخته برای هر کدوم از اون block ها که گفتم که از الان بهش میگید chunk به هرکدوم از اون block ها میگید chunk! اینطوریه:
typedef struct SMemoryChunk
{
TByte *Data ; // The actual Data
std::size_t DataSize ; // Size of the "Data"-Block
std::size_t UsedSize ; // actual used Size
bool IsAllocationChunk ; // true, when this MemoryChunks
// Points to a "Data"-Block
// which can be deallocated via "free()"
SMemoryChunk *Next ; // Pointer to the Next MemoryChunk
// in the List (may be NULL)
} SMemoryChunk ;
میبینید دیگه هر chunk دارای اشاره گر به :
1.یک قسمت کوچیک از حافظست
2.کل اندازه حافظه ای که از ابتدای pool تا جایه chunk فعلی داده شده.
3.مقدار واقعی که استفاده شده
4.اشاره گر به chunk بعدی
قدم اول طبق گفته پیش تعریف کردن حافظه مورد نیاز!
خوب ما یک کلاس memory pool داریم که میسازیمش و مقدار دهیش میکنیم در سازندش و یه فضایی را اختصاص میدیم:
/******************
Constructor
******************/
CMemoryPool::CMemoryPool(const std::size_t &sInitialMemoryPoolSize,
const std::size_t &sMemoryChunkSize,
const std::size_t &sMinimalMemorySizeToAllocate,
bool bSetMemoryData)
{
m_ptrFirstChunk = NULL ;
m_ptrLastChunk = NULL ;
m_ptrCursorChunk = NULL ;
m_sTotalMemoryPoolSize = 0 ;
m_sUsedMemoryPoolSize = 0 ;
m_sFreeMemoryPoolSize = 0 ;
m_sMemoryChunkSize = sMemoryChunkSize ;
m_uiMemoryChunkCount = 0 ;
m_uiObjectCount = 0 ;
m_bSetMemoryData = bSetMemoryData ;
m_sMinimalMemorySizeToAllocate = sMinimalMemorySizeToAllocate ;
// Allocate the Initial amount of Memory from the Operating-System...
AllocateMemory(sInitialMemoryPoolSize;
}











