Materiál článku je převzat z mého .

V minulosti , slíbil jsem posoudit otázku hodnocení zátěže na tickeru a metody boje s nadměrnou výpočetní zátěží v mediálním streameru. Ale rozhodl jsem se, že bude logičtější osvětlit otázky ladění tvorby filtrů, které se vztahují k přesunu dat, a teprve poté probrat otázky optimalizace výkonu.
Ladění tvorby filtrů
Po tom, co jsme v předchozím článku prozkoumali mechanismus přesunu dat v mediálním streameru, bude logické mluvit o rizicích, která se v něm skrývají. Jednou z charakteristik principu "data flow" je to, že alokace paměti z haldy probíhá ve filtrech, které se nacházejí na počátku datového toku, zatímco uvolnění paměti zpět do haldy provádějí filtry umístěné na konci cesty toku. Kromě toho může vytváření nových dat a jejich zničení probíhat někde v mezích. V obecném případě paměť uvolňuje ten filtr, který nevytvořil blok dat.
Z hlediska průhledného monitorování paměti by bylo rozumné, aby filtr, který přijímá vstupní blok, po zpracování okamžitě zničil jej s uvolněním paměti, a na výstup by vystavoval nově vytvořený blok s výstupními daty. V tomto případě by únik paměti ve filtru snadno sledoval – pokud analyzátor zjistí únik v filtr, znamená to, že následující filtr nezničil příchozí bloky řádně a chyba je v něm. Ale z pohledu udržení vysokého výkonu není takový přístup k práci s datovými bloky produktivní – vede to k velkému množství operací alokace/uvolnění paměti pro datové bloky bez jakéhokoli užitečného výstupu.
Z tohoto důvodu filtry mediastreamu, aby nezpomalily zpracování dat, při kopírování zpráv používají funkce vytvářející lehké kopie (o nich jsme hovořili v předchozím článku). Tyto funkce pouze vytvářejí nový instance hlavičky zprávy „připevněním“ k ní bloku dat od kopírované „staré“ zprávy. Výsledkem je, že k jednomu bloku dat jsou přiřazeny dvě hlavičky a zvyšuje se počítadlo odkazů v bloku dat. To však bude vypadat jako dvě zprávy. Zpráv s takovým „sdíleným“ blokem dat může být více, například filtr MS_TEE generuje najednou desítku takových lehkých kopií a rozděluje je na svých výstupech. Při správné funkci všech filtrů v řetězci by toto počítadlo odkazů mělo na konci linky dosáhnout nuly a bude vyvolána funkce uvolnění paměti: ms_free(). Pokud k vyvolání nedojde, znamená to, že tento kus paměti se již nevrátí do haldy, tedy „unikne“. Cenou za používání lehkých kopií je ztráta možnosti snadno zjistit (jak by tomu bylo v případě použití běžných kopií), v kterém filtru graf uniká paměť.
Jelikož odpovědnost za vyhledávání úniků paměti v „nativních“ filtrech leží na vývojářích mediastreamu, pravděpodobně je nebudete muset ladit. Co se týče vašeho vlastního filtru – jste vlastním kovářem svého štěstí a od vaší pečlivosti závisí čas, který strávíte hledáním úniků ve svém kódu. Abychom zkrátili vaši dobu mučení s laděním, měli bychom zvážit techniky lokalizace úniků při vývoji filtrů. Může se také stát, že únik se projeví pouze při použití filtru v reálném systému, kde by počet „podezřelých“ mohl být obrovský, a čas na ladění omezený.
Jak se projevuje únik paměti?
Logicky by se dalo předpokládat, že v výstupu programu top se bude zobrazovat rostoucí procento paměti, kterou zabírá vaše aplikace.
Externí projevy se budou projevovat tím, že systém v určitém okamžiku začně pomalu reagovat na pohyb myši, pomalu se př redrawovat obrazovku. Může také růst systémový log, žeroucí místo na pevném disku. Vaše aplikace začne podivně fungovat, nebude reagovat na příkazy, nebude moci otevřít soubor atd.
Abychom zjistili, zda došlo k úniku paměti, použijeme analyzátor paměti (dále jen analyzátor). Může to být Valgrind (dobrý o něm) nebo zabudovaný v kompilátoru gcc nebo něco jiného. Pokud analyzátor ukáže, že únik se vyskytuje v jednom z filtrů grafu, znamená to, že bychom měli použít jednu z níže popsaných metod.
Metoda tří borovic
Jak bylo uvedeno výše, při úniku paměti analyzátor ukáže na filtr, který požádal o přidělení paměti z haldy. Ale neukáže na filtr, který "zapomněl" ji vrátit, což je vlastně viníkem. Analyzátor tedy může pouze potvrdit naše obavy, ale neukázat na jejich kořen.
Abychom zjistili polohu "špatného" filtru v grafu, můžeme zkrátit graf na minimální počet uzlů, při kterém analyzátor stále detekuje únik, a v zbývajících třech borovicích lokalizovat problémový filtr.
Může se však stát, že se zkrácením počtu filtrů v grafu naruší obvyklý průběh interakce filtrů s jinými prvky vaší systému a únik přestane být patrný. V takovém případě bude nutné pracovat s plnou velikostí grafu a použít přístup, který je uveden níže.
Metoda klouzavého izolátoru
Pro jednoduchost zobrazení použijeme graf, který se skládá z jedné řetězce filtrů. Ten je zobrazen na obrázku.
![]()
Běžný graf, ve kterém byly vedle hotových filtrů mediastreamera použity čtyři krafťácké filtry F1…F4, čtyř různých typů, které jste vytvořili před nějakou dobou a o jejichž správnosti nemáte pochyb. Přesto předpokládejme, že v několika z nich došlo k úniku paměti. Spuštěním našeho programu pro dohled analyzátoru se z jeho zprávy dozvíme, že určitý filtr požádal o určité množství paměti a nevrátil ji do haldy N-krát. Snadno si domyslíme, že bude odkaz na interní funkce filtru typu MS_VOID_SOURCE. Jeho úkolem je získávat paměť z haldy. Vrátit ji zpět by měly jiné filtry. Tedy zjistíme, že k úniku došlo.
Abychom zjistili, na jakém místě linky došlo k nečinnosti, která vedla k úniku paměti, navrhujeme zavést další filtr, který prostě přehazuje zprávy z vstupu na výstup, přičemž vytváří nikoli lehkou, ale „těžkou“ kopii vstupní zprávy a poté plně maže zprávu, která přišla na vstup. Takový filtr budeme nazývat izolátorem. Předpokládáme, že protože je filtr jednoduchý, únik v něm je vyloučen. A další pozitivní vlastnost — pokud ho přidáme na jakékoli místo v našem grafu, nebude to mít žádný vliv na fungování schématu. Budeme znázorňovat filtr-izolátor jako kruh s dvojitou čárou.
Zapneme izolátor hned po filtru voidsource:
![]()
Opět spouštíme program s analyzátorem a vidíme, že tentokrát analyzátor vinu přenese na izolátor. Vždyť to on nyní vytváří datové bloky, které pak mizí neznámému nedbalému filtru (nebo filtrům). Dalším krokem posuneme izolátor po řetězci doprava o jeden filtr a znovu spustíme analýzu. Tak, krok za krokem posouváním izolátoru doprava, dosáhneme situace, kdy v dalším reportu analyzátoru se počet „uteklých“ bloků paměti sníží. To znamená, že v tomto kroku se izolátor ocitl v řetězci těsně za problematickým filtrem. Pokud byl „špatný“ filtr jediný, únik úplně zmizí. Tímto způsobem jsme lokalizovali problémový filtr (nebo jeden z několika). „Opravením“ filtru můžeme pokračovat v posouvání izolátoru doprava v řetězci až do úplného vítězství nad úniky paměti.
Implementace filtru-izolátoru
Implementace izolátoru vypadá jako běžný filtr. Hlavičkový soubor:
/* Файл iso_filter.h Описание изолирующего фильтра. */
#ifndef iso_filter_h
#define iso_filter_h
/* Задаем идентификатор фильтра. */
#include <mediastreamer2/msfilter.h>
#define MY_ISO_FILTER_ID 1024
extern MSFilterDesc iso_filter_desc;
#endif
Samotný filtr:
/* Файл iso_filter.c Описание изолирующего фильтра. */
#include "iso_filter.h"
static void
iso_init (MSFilter * f)
{
}
static void
iso_uninit (MSFilter * f)
{
}
static void
iso_process (MSFilter * f)
{
mblk_t *im;
while ((im = ms_queue_get (f->inputs[0])) != NULL)
{
ms_queue_put (f->outputs[0], copymsg (im));
freemsg (im);
}
}
static MSFilterMethod iso_methods[] = {
{0, NULL}
};
MSFilterDesc iso_filter_desc = {
MY_ISO_FILTER_ID,
"iso_filter",
"A filter that reads from input and copy to its output.",
MS_FILTER_OTHER,
NULL,
1,
1,
iso_init,
NULL,
iso_process,
NULL,
iso_uninit,
iso_methods
};
MS_FILTER_DESC_EXPORT (iso_desc)
Metoda přepsání funkcí správy paměti
Pro podrobnější zkoumání je v mediastreameru k dispozici možnost přepsání funkcí přístupu k paměti na vaše vlastní, které kromě hlavní práce zaznamenávají "Kdo, kam a proč". Přepisují se tři funkce. Provádí se to následujícím způsobem:
OrtpMemoryFunctions reserv;
OrtpMemoryFunctions my;
reserv.malloc_fun = ortp_malloc;
reserv.realloc_fun = ortp_realloc;
reserv.free_fun = ortp_free;
my.malloc_fun = &my_malloc;
my.realloc_fun = &my_realloc;
my.free_fun = &my_free;
ortp_set_memory_functions(&my);Tato možnost je užitečná v situacích, kdy analyzátor zpomaluje práci filtrů natolik, že narušuje funkci systému, do něhož je naše schéma integrováno. V takové situaci je nutné se vzdát analyzátoru a použít přepis funkcí správy paměti.
Probrali jsme algoritmus akcí pro jednoduchý graf, který neobsahuje rozvětvení. Tento přístup lze však použít i pro jiné případy, samozřejmě s komplikacemi, ale myšlenka zůstane stejná.
V následujícím článku se budeme zabývat otázkou hodnocení zatížení tickeru a způsoby, jak se vypořádat s nadměrnou výpočetní zátěží v mediastreameru.
Zdroj: habr.com
