VisualDSP работи с динамично разпределена памет, dsp, програмиране

Библиотеката за изпълнение на Blackfin C/C ++ съдържа 4 стандартни функции за управление на купчината: calloc, free, malloc и realloc. По подразбиране приложението определя една купчина, така наречената купчина по подразбиране (купчина по подразбиране), който обслужва всички заявки за динамично разпределение на блокове памет, където някои алтернативни купчини не са изрично посочени. Купчината по подразбиране е дефинирана в стандартния файл с описание на свързващия файл (* .ldf) и заглавката на времето за изпълнение.

Ако една купчина по подразбиране е достатъчна, не са необходими допълнителни стъпки, за да започнете да използвате динамично разпределение на паметта - т.е. функциите calloc, free, malloc и realloc са налични веднага след стартирането на приложението. Може да се наложи само да промените параметрите (например размера) на системната купчина. Това се прави в настройките на проекта, раздел LDF Settings -> System Heap.

Може да се дефинира произволен брой допълнителни купчини. Тези купчини ще обслужват тези заявки за разпределение, които изрично посочват конкретна купчина. Тези допълнителни купчини могат да бъдат съобщени чрез разширените функции за разпределение heap_calloc, heap_free, heap_malloc и heap_realloc. Има и допълнителна функция heap_space_unused, вижте по-долу за подробности.

Използването на множество купчини позволява на програмиста да избира между блокова памет - или да използва бърза, но скъпа памет (SRAM, L1), или да използва по-бавна, но достатъчно памет (SDRAM, L3). Това важи особено за проектите VDK, където API-тата активно използват купчината на системата за създаване на нишки.

Следващото ще ви покаже как да дефинирате купчината, как да работите с купчини, как да използвате интерфейса на купчината и как да освободите място на купчината.

[Определяне на купчината]

Купчината може да бъде дефинирана или на етапа на изграждане на проекта за приложение (време за връзка), или по време на изпълнението му (време на изпълнение). И в двата случая купчината има 3 атрибута:

Системният куп по подразбиране, дефиниран от времето за връзка, винаги има идентификатор на потребителя 0.

Освен това купчините се индексират. Това е нещо като идентификатор на потребителя, но разликата е, че индексът не се присвоява от потребителя, а от системата. Всички разпределения и освобождавания на блокове памет използват индексите на купчината, но не и идентификационния номер на купчината. Стойността на потребителския идентификатор на купчина може да бъде преобразувана в нейния индекс чрез извикване с помощта на _heap_lookup () (вижте "Определяне на купчини по време на изграждане (време на връзка)"). Бъдете внимателни, за да сте сигурни, че сте предали правилния идентификатор на всяка функция.

[Определяне на купчини по време на изграждане (Link-Time)]

Ето стъпка по стъпка процес за създаване на купчина в L3 памет (SDRAM).

един. Отворете свойствата на проекта (Меню на проекта -> Опции на проекта.).

2. Отидете на LDF Settings -> User Heap.

3. В полето за въвеждане на "Heap name:" въведете име за новата купчина. Това име трябва да отговаря на изискванията на имена C, т.е.не трябва да съдържа интервали или други специални символи.

4. В списъка „Типове памет:“ изберете типа памет, където ще се създава купчината.

пет. Посочете размера на купчината - "Размер на купчината:".

динамично

Адресите, които се появяват в тази таблица, могат да бъдат буквални (изрично посочени като числа) или могат да бъдат символи, които се разпознават от свързващия елемент. Купчината по подразбиране използва символи, генерирани от линкера, използвайки .ldf файла.

Таблицата _heap_ трябва да бъде разпределена в постоянна памет. Използва се за инициализиране на структурата на време за изпълнение ___heaps, когато е направена първата заявка към купчината. Когато паметта се разпределя от която и да е купчина, библиотеката инициализира структурата ___heaps, използвайки данните в таблицата _heap_ и задава ___nheaps на броя на наличните купчини.

Тъй като функциите за разпределение използват индекси на купчина вместо техните идентификатори на потребителите, купчините, конфигурирани по този начин, трябва да имат идентификатор на потребителя, свързан с индексите, преди да ги използват изрично:

[Дефиниране на купчини по време на изпълнение]

Купчините могат да бъдат дефинирани и инсталирани по време на изпълнение с помощта на функцията _heap_install ():

Тази функция може да вземе всяка част от паметта и да започне да я използва като купчина. Функцията ще върне индекса на купчината, която е била разпределена като новоинсталирана купчина, или ще върне отрицателна стойност, ако са открити проблеми с разпределението на купчината (вижте "Съвети за работа с купчини").

Причините, поради които _heap_install () може да се провали, са както следва (но може да има и други причини, които не са изброени тук):

• Вече има купчина, в която е зададен посоченият идентификатор на потребителя.
• Новата купчина е твърде малка за използване (посоченият параметър за дължина е твърде малък).

[Съвети за работа с купчини]

Размерите на купчината (параметър за дължина) трябва да бъдат цели числа, делими на степен две, за да се използва паметта по-ефективно. Разпределителят на купчина работи с размери на блокове като 256, 512 или 1024 байта.

За съвместимост с C ++, извикванията към malloc и calloc с размер на блока 0 ще разпределят блок с размер 1.

[Стандартен интерфейс за купчина]

Стандартните функции calloc и malloc ще разпределят нов обект (блок памет) от купчината по подразбиране. Ако realloc е извикан с нулев указател, тогава резултатът ще бъде присвоен и нов обект от купчината по подразбиране.

По-рано разпределените обекти могат да бъдат освободени с функциите free или realloc. Когато предварително разпределен обект промени размера си чрез извикване на realloc, върнатият обект ще бъде в същата купчина като оригиналния обект.

Функцията space_unused ще върне броя на неразпределените блокове в купчината с индекс 0. Обърнете внимание, че няма да можете да разпределите цялото това пространство, защото има разходи за фрагментиране на купчината и поддържане на купчина.

[Разпределяне на C ++ STL обекти от купчината, която не е по подразбиране]

C ++ STL обектите могат да се поставят върху купчината, която не е по подразбиране, като се използва персонализиран разпределител. За да направите това, първо трябва да създадете персонализиран разпределител. По-долу е даден пример за персонализиран разпределител, който можете да използвате като основа за своя собствена. Най-важната част от customalloc.h в повечето случаи е функцията за разпределение на паметта, където паметта се разпределя за STL обект. В момента приложимият ред код присвоява купчина по подразбиране (0):

Ако просто промените първия параметър на heap_malloc (), тогава можете да разпределите памет от друга купчина:

• 0 отговаря на купчина по подразбиране (купчина по подразбиране).
• 1 е първият потребителски куп.
• 2 е втората купчина потребители.
•. и т.н.

След като създадете своя персонализиран разпределител, трябва да уведомите STL обекта, който се използва, за да го използва. Ето пример за стандартна дефиниция "списък":

Същото ще се случи, ако напишете:

Тук "разпределител" е разпределителят по подразбиране. По този начин можем да кажем на списък "a" да използва персонализиран разпределител като този:

След като бъде създаден, списъкът "а" може да се използва по същия начин, както е създаден по обичайния начин. Страничната лента по-долу показва пример за използване на персонализиран разпределител (код example.cpp).

[customalloc.h]

[example.cpp]

[Използване на алтернативен интерфейс към купчината]

Както бе споменато, библиотеката C в реално време предоставя няколко алтернативни функции за достъп до купчината: heap_calloc, heap_free, heap_malloc и heap_realloc. Тези функции работят точно като традиционните стандартни, с изключение на допълнителен аргумент, който указва индекса на купчината, който да се използва.

Ето прототипите за тези функции:

Имената на реални функции имат първоначална долна черта пред името на функцията, но заглавният файл stdlib.h дефинира макроси, които указват имена на функции без тази долна черта.

Имайте предвид, че обаждането

Въпреки това, да се обадите

където ptr! = NULL, предоставеният параметър idx се игнорира; преразпределението на паметта винаги ще се извършва в купчината, където е бил разпределен блокът, посочен от ptr, дори ако memcpy е необходим за преместване на данни за купчината.

По същия начин предизвикателството

игнорира предоставения параметър idx index, който е посочен само за целостта на интерфейса - блокът, посочен от ptr, винаги ще бъде освободен от купчината, в която е бил разпределен.

Функцията heap_space_unused (int idx) ще върне броя байтове, останали неразпределени в купчината при idx. Ако тази функция връща -1, тогава купчината с този индекс не съществува.

[Поддръжка на C ++ за интерфейс за алтернативна купчина]

Библиотеката за изпълнение на C ++ осигурява поддръжка за разпределяне и освобождаване на памет от алтернативни купчини чрез новите оператори и операторите за изтриване.

Купчините трябва да бъдат инициализирани с функции за изпълнение на C, както е описано по-рано. След това тези купчини могат да се използват чрез традиционния механизъм new и delete, като просто се предаде индексът на купчината на новия оператор. Операторът за изтриване не трябва да предава индекса на купчина, тъй като тази информация не е необходима за освобождаване на паметта.

По-долу е даден пример за това как работи.

[Освобождаване на памет]

Когато даден блок памет е „освободен“ (чрез извикване на свободен или изтрит), той не се връща в „системата“. Вместо това се съхранява в безплатния списък на въпросната купчина. Освободените блокове се обединяват, ако е възможно.

Можете да реинициализирате купчината, като извикате функцията _heap_init, като по този начин изчистите безплатния списък. В този случай цялата памет на купчината ще бъде освободена и дупки в нея ще бъдат премахнати, ако са се образували там след множество разпределения и освобождаване на памет.

Ако успее, функцията _heap_init ще върне 0 и ако повикването е неуспешно, ще бъде върната ненулева стойност. Имайте предвид обаче, че повикването все още ще обезсили всички записи в купчината, така че _heap_init не може да се използва, ако в купчината има валидни разпределения.

[Как се използва купчина в приложения на VDK]

Всеки знае често срещаните проблеми, свързани с динамичното разпределение и освобождаване на памет в купчината - това са изтичане на памет за кухини в купчината и времето на процесора, прекарано от събирача на боклука. Следователно, отделен механизъм за разпределяне и освобождаване на блокове памет е добавен към VDK - пулове памет (за повече подробности вижте [3]).

Традиционните функции за работа с купчини (calloc, free, malloc, realloc, heap_calloc, heap_free, heap_malloc, heap_realloc, new, delete) също са налични в приложенията VDK. Имайте предвид обаче, че библиотеките на API на VDK активно използват купчината по подразбиране за обслужване на нишки (system_heap). Когато се създава нишка, паметта се разпределя от системната купчина, така че когато се създават голям брой нишки, системната купчина може бързо да препълни (функциите CreateThread, CreateThreadEx ще върнат грешката kThreadCreationFailure). Въпреки това, за да се запази паметта на системната купчина, е възможно да се конфигурират допълнителни, алтернативни купчини за VDK нишки.

Забележка: Функциите на библиотеката VisualDSP могат да консумират памет на системната купчина и няма документация за това. Например стандартната функция вход/изход printf при първа употреба разпределя 552 байта памет в системната купчина. Тази памет не се освобождава, докато приложението не приключи, но последващите извиквания printf вече не разпределят допълнителна памет.

Създаване на нова купчина VDK. Можете да създадете нова купчина в интерфейса за настройки на ядрото на проекта VDK (раздел Ядро -> Купчини -> изберете Нова купчина от контекстното меню). След това посочете потребителския идентификатор за създадената купчина - произволен номер, който след това може да се използва за извикване на функции heap_calloc, heap_free, heap_malloc, heap_realloc и нови оператори.

памет

След създаването на купчината може да се използва за всеки тип нишка - в свойствата на типа нишка можете да изберете купчината от падащите списъци Структура на нишка и Купчина на стека. Имайте предвид обаче, че новата купчина ще работи само след като бъде правилно инициализирана.

Когато конфигурирате купчината за VDK, тя не отменя купчината, която традиционните повиквания на malloc използват. Просто създавате идентификатор за купчината и тази купчина може след това да бъде посочена като купчината за купчината на структурата на нишките и самата купчина на стека, вижте екранните снимки по-долу.