Materijal u članku preuzet je sa mog .

V prejšnjem , obljubil sem, da bom preučil vprašanje ocene obremenitve na ticker in načine za boj proti pretirani računalniški obremenitvi v medijskem strežniku. A sem se odločil, da bo smiselneje osvetliti vprašanja odpravljanja napak pri ročnih filtrih, povezanih s prenosom podatkov, in šele nato obravnavati vprašanja optimizacije zmogljivosti.
Odpravljanje napak pri ročnih filtrih
Po tem, ko smo v prejšnjem članku obravnavali mehanizem prenosa podatkov v medijskem strežniku, se zdi smiselno govoriti o nevarnostih, ki se skrivajo znotraj njega. Ena izmed značilnosti načela "data flow" je, da se dodeljevanje pomnilnika iz skladišča dogaja v filtrih, ki so na izvoru podatkovnega toka, medtem ko se osvobajanje pomnilnika z vračanjem v skladišče izvaja v filtrih, ki so na koncu poti toka. Poleg tega se lahko ustvarjanje novih podatkov in njihovo uničenje zgodi kjerkoli na vmesnih točkah. Na splošno osvobodi pomnilnik ne tisti filter, ki je ustvaril blok podatkov.
Z vidika pregleda pomnilnika bi bilo smiselno, da filter, ko prejme vhodni blok, takoj po obdelavi uniči in osvobodi pomnilnik, na izhodu pa predstavi spet ustvarjen blok z izhodnimi podatki. V tem primeru bi bila uhajanja pomnilnika v filtrih enostavno sledljiva — če analitik odkrije uhajanje v filtru, to pomeni, da naslednji filter ne uniči prihajajočih blokov ustrezno in je napaka v njem. Vendar pa z vidika ohranjanja visoke zmogljivosti tak pristop k delu z bloki podatkov ni produktiven — vodi do velikega števila operacij dodeljevanja/oslobađenja pomnilnika za bloke podatkov brez kakršnega koli koristnega izhoda.
Iz tega razloga filtri medijskega pretakalnika, da ne bi upočasnili obdelave podatkov, pri kopiranju sporočil uporabljajo funkcije, ki ustvarjajo lahke kopije (o katerih smo govorili v prejšnjem članku). Te funkcije samo ustvarjajo nov primerek glave sporočila, ki se "priključi" na blok podatkov iz kopiranega "starega" sporočila. Posledično sta na en blok podatkov povezana dve glavi, kar aktivira inkrement števcem sklicev v bloku podatkov. Vendar pa bo to izgledalo kot dve sporočili. Sporočil s takim "javnim" blokom podatkov je lahko več, na primer filter MS_TEE takoj ustvari deset takih lahkih kopij in jih razporedi po svojih izhodih. Pri pravilnem delovanju vseh filtrov v verigi bi moral na koncu traku ta števec sklicev doseči nič in se bo aktivirala funkcija za sproščanje pomnilnika: ms_free(). Če se klica ne zgodi, to pomeni, da se ta del pomnilnika ne bo vrnil v kup, t.j. bo "uhajal". Plačilo za uporabo lahkih kopij je izguba možnosti, da bi enostavno ugotovili (kot bi bilo pri uporabi običajnih kopij), v katerem filtru graf uhaja pomnilnik.
Ker odgovornost za iskanje uhajanj pomnilnika v "materinskih" filtrih leži na razvijalcih medijskega pretakalnika, vam verjetno ne bo treba jih odpravljati. Ampak z vašim ročno izdelanim filtrjem — vi ste sami kovač svoje sreče in od vaše skrbnosti bo odvisno, koliko časa boste porabili za iskanje uhajanj v vašem kodi. Da bi skrajšali vaša trudna iskanja z odpravljanjem napak, moramo obravnavati tehnike lokalizacije uhajanj pri razvoju filtrov. Poleg tega lahko pride do tega, da se uhajanje pojavi šele pri uporabi filtra v realnem sistemu, kjer se lahko število "sumljivih" izkaže za ogromno, čas za odpravljanje napak pa omejen.
Kako se kaže uhajanje pomnilnika?
Logično bi bilo domnevati, da se v izhodu programa top prikazuje naraščajoči odstotek pomnilnika, ki ga zaseda vaša aplikacija.
Zunanja manifestacija se bo kazala v tem, da bo sistem ob določenem trenutku začel počasi reagirati na gibanje miške, počasi ponovno risati zaslon. Morda se bo tudi povečeval sistemski dnevnik, kar bo zasedalo prostor na trdem disku. Hkrati se bo vaše aplikacije začela obnašati nenavadno, ne bo odgovarjala na ukaze, ne bo mogla odpreti datoteke itd.
Za ugotavljanje nastanka puščanja bomo uporabili analizator pomnilnika (v nadaljevanju analizator). To je lahko Valgrind (dober o njem) ali vgrajen v prevajalnik gcc ali kaj drugega. Če analizator pokaže, da puščanje nastane v enem od filtrov grafa, to pomeni, da je čas, da uporabimo eno od načinov, opisanih spodaj.
Metoda treh borovcev
Kot je bilo že omenjeno, bo analizator ob puščanju pomnilnika pokazal na filter, ki je zahteval dodelitev pomnilnika iz kupčka. Vendar ne bo pokazal na filter, ki je "pozabil" vrniti spomin, kar je v resnici krivec. Tako analizator lahko le potrdi naše skrbi, ne more pa pokazati njihovega izvora.
Da bi ugotovili lokacijo "nepravilnega" filtra v grafu, lahko sledite poti skrajševanja grafa do minimalnega števila vozlišč, pri čemer analizator še vedno odkrije puščanje in v preostalih treh borovcih lokalizirate problematični filter.
Vendar se lahko zgodi, da boste pri zmanjšanju števila filtrov v grafu motili običajni potek interakcije filtrov z drugimi elementi vašega sistema in se puščanje ne bo več kazalo. V tem primeru boste morali delati s polno velikostjo grafa in uporabiti pristop, ki je opisan spodaj.
Metoda drsnega izolatorja
Za enostavnost razlage uporabimo graf, ki se sestoji iz verige filtrov. Ta je prikazana na sliki.
![]()
Navaden graf, v katerem poleg pripravljenih filtrov medijskega pretakalnika uporabljamo štiri ročne filtre F1…F4, različnih tipov, ki ste jih izdelali že zdavnaj in o njihovi pravilnosti ne dvomite. Kljub temu predpostavimo, da v nekaterih izmed njih obstaja uhajanje pomnilnika. Ko zaženemo našo programsko opremo za nadzor analizerja, iz njegovega poročila izvemo, da je določen filter zahteval nek znesek pomnilnika in ga ni vrnil v kup N-krat. Lahko zlahka uganemo, da bo sklicevanje na notranje funkcije filtra vrste MS_VOID_SOURCE. Njegova naloga je, da pridobi pomnilnik iz kupa. Vrniti bi ga morali drugi filtri. To pomeni, da bomo odkrili dejstvo uhajanja.
Da bi določili, na katerem delu traku je prišlo do neaktivnosti, ki je povzročila uhajanje pomnilnika, predlagamo uvedbo dodatnega filtra, ki preprosto prenaša sporočila s vhodne na izhodno stran, vendar pri tem ustvari težko kopijo vhodnega sporočila, nato pa popolnoma izbriše sporočilo, ki je prišlo na vhod. Ta filter bomo imenovali izolator. Predvidevamo, da ker je filter preprost, uhajanja v njem ni. Še ena pozitivna lastnost — če ga dodamo na katerikoli del našega grafa, to ne bo vplivalo na delovanje sheme. Filter-izolator bomo prikazali kot krog z dvojno obrobo.
Vklopimo izolator takoj za filtrom voidsource:
![]()
Spet zaženemo program z analizerjem in vidimo, da tokrat analizer obtožuje izolator. Saj on zdaj ustvarja podatkovne bloke, ki se nato izgubljajo zaradi neznanega malomarnega filtra (ali filtrov). Naslednji korak je, da pomaknemo izolator po verigi desno, do enega filtra in ponovno zaženemo analizo. Tako, korak za korakom, premikamo izolator desno, dokler ne dobimo situacije, ko se v naslednjem poročilu analizerja zmanjša število "uhajajočih" blokov pomnilnika. To pomeni, da je v tem koraku izolator končal v verigi takoj za problematičnim filtrom. Če je bil "slab" filter eden, bo uhajanje sploh izginilo. Tako smo lokalizirali problematični filter (ali enega izmed več). Ko "popravimo" filter, lahko nadaljujemo z gibanjem izolatorja desno po verigi do popolne zmage nad uhajanjem pomnilnika.
Implementacija filtra-izolatorja
Izvedba izolatorja je videti kot navaden filter. Glava:
/* Файл 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
Sam filter:
/* Файл 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 zamenjave funkcij za upravljanje pomnilnika
Za bolj natančne raziskave ima medijski strežnik možnost zamenjave funkcij dostopa do pomnilnika z vašimi lastnimi, ki poleg osnovnega dela beležijo "Kdo, kam in zakaj". Zamenjane so tri funkcije. To izvedemo na naslednji način:
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);Takšna možnost pride prav v primerih, ko analitik upočasni delovanje filtrov toliko, da krši delovanje sistema, v katerega je vgrajena naša shema. V taki situaciji se je treba odpovedati analitiku in uporabiti zamenjavo funkcij za delo s pomnilnikom.
Obravnavali smo algoritem delovanja za preprost graf, ki ne vsebuje razvejanosti. Toda ta pristop lahko uporabimo tudi v drugih primerih, seveda z zapletenostjo, vendar bo ideja ostala enaka.
V naslednjem članku bomo obravnavali vprašanje ocene obremenitve na tickerju in načine za obvladovanje prekomerne računalniške obremenitve v medijskem pretakalniku.
Izvor: habr.com
