Materialet i artiklen er taget fra min .

I den forrige , jeg lovede at se på vurderingen af belastningen på tickeren og måder at bekæmpe overflødig beregningsbelastning i mediastreameren. Men jeg besluttede, at det ville være mere logisk at belyse problemerne med fejlfinding af craft-filtre, der vedrører dataflytning, og derefter tage fat på optimering af ydeevnen.
Fejlfinding af craft-filtre
Efter vi i den tidligere artikel har set på mekanismen bag dataflytning i mediastreameren, vil det være logisk at tale om de skjulte farer, der findes i den. En af karakteristikaene ved "data flow" princippet er, at hukommelsesallokering fra bunken sker i de filtre, der er ved kilden til dataflowet, mens frigivelsen af hukommelse tilbage til bunken foretages af filtre placeret i slutningen af dataflowet. Derudover kan oprettelse og destruktion af nye data finde sted på steder imellem. Generelt udfører det ikke det filter, der har skabt datablokken, hukommelsesfrigivelsen.
Set i lyset af gennemsigtig overvågning af hukommelsen ville det være fornuftigt, at filteret, når det modtager en indgangsblok, straks destruerede den efter behandling med frigivelse af hukommelse, og på outputen ville det præsentere den nyoprettede blok med uddata. I dette tilfælde ville hukommelseslækage i filteret nemt kunne spores — hvis analysatoren opdager en lækage i filteret, så skyldes det, at det efterfølgende filter ikke destruerer indgående blokke korrekt, og fejlen ligger hos det. Men fra et synspunkt om at opretholde høj ydeevne, er en sådan tilgang til arbejdet med datablokke ikke produktiv — det fører til et stort antal operationer med hukommelsesallokering/frigivelse af datablokke uden noget nyttigt output.
Af denne grund bruger mediastream-filtere, for ikke at forsinke databehandlingen, funktioner til at lave lette kopier ved kopiering af beskeder (vi har beskrevet dem i den tidligere artikel). Disse funktioner skaber kun et nyt eksempel af beskedens header ved 'at fastgøre' det til data-blokken fra den kopierede 'gamle' besked. Som resultat vil to headers være knyttet til én datablok, og opdaterings-tælleren for referencer i datablokken vil blive udført. Men det vil se ud som to beskeder. Der kan være flere beskeder med sådan en 'deling' af datablokken; for eksempel genererer MS_TEE-filteret straks ti sådanne lette kopier, der distribueres til sine udgange. Ved korrekt drift af alle filtre i kæden skal denne referencetæller nå nul ved slutningen af processen, og funktionen til frigivelse af hukommelse vil blive kaldt: ms_free(). Hvis kaldet ikke sker, betyder det, at denne del af hukommelsen allerede ikke vil returnere til bunken, dvs. den 'lækker'. Omkostningen ved at bruge lette kopier er tabet af muligheden for let at identificere (som det ville være tilfældet med almindelige kopier), hvilket filter hukommelsen lækker ud.
Da ansvaret for at finde hukommelseslækager i 'native' filtre ligger hos udviklerne af mediastreameren, vil du sandsynligvis ikke skulle fejlfinde dem. Men med dit skræddersyede filter — du er selv smeden af din lykke, og din omhu vil bestemme den tid, du bruger på at finde lækager i din kode. For at reducere din tid med fejlfinding, skal vi overveje teknikker til lokalisering af lækager under udviklingen af filtre. Desuden kan det ske, at en lækage kun viser sig, når filteret anvendes i et reelt system, hvor antallet af 'mistænkte' kan blive enormt, og tiden til fejlfinding begrænset.
Hvordan viser en hukommelseslækage sig?
Det er logisk at antage, at top programoutput vil vise en stigende procentdel af hukommelse, der bruges af din applikation.
Det synlige symptom vil være, at systemet på et tidspunkt begynder at reagere langsomt på musebevægelser og langsomt opdaterer skærmen. Systemloggen kan også vokse og optage plads på harddisken. Samtidig vil din applikation begynde at opføre sig mærkeligt, ikke reagere på kommandoer, ikke kunne åbne filer osv.
For at afsløre forekomsten af en hukommelseslækage vil vi bruge en hukommelsesanalysert (fremover kaldet analysator). Dette kan være Valgrind (god om det) eller indbygget i compileren gcc eller noget andet. Hvis analysatoren viser, at lækagen opstår i en af grafens filtre, så betyder det, at det er tid til at anvende en af metoderne nedenfor.
Metode med tre tragte
Som tidligere nævnt vil analysatoren pege på det filter, der anmodede om at få tildelt hukommelse fra bunken, men vil ikke pege på det filter, der "glemte" at returnere det, som i virkeligheden er den skyldige. Derved kan analysatoren kun bekræfte vores mistanker, men ikke pege på roden af problemet.
For at finde placeringen af det "skadelige" filter i grafen kan man forkorte grafen til et minimalt antal noder, hvor analysatoren stadig opdager lækagen, og i de resterende tre tragte lokaliserer det problematiske filter.
Men det kan ske, at når man reducerer antallet af filtre i grafen, bryder man den normale interaktion mellem filtrene og andre elementer i systemet, og lækagen stopper med at vise sig. I så fald må man arbejde med den fulde graf og anvende den tilgang, der er beskrevet nedenfor.
Metode med glidende isolator
For enkelheds skyld vil vi bruge en graf, der består af en kæde af filtre. Den er vist på tegningen.
![]()
En almindelig graf, hvor der sammen med de færdige filtre fra mediestrømmeren anvendes fire skræddersyede filtre F1…F4, fire forskellige typer, som du har lavet for længe siden og er sikker på i deres korrekthed. Antag dog, at der er hukommelseslækager i nogle af dem. Ved at køre vores overvågningsprogram til analysatoren, får vi at vide fra dens rapport, at et bestemt filter har anmodet om en vis mængde hukommelse og ikke har returneret det til bunken et N-antal gange. Det er let at gætte, at der vil være en henvisning til de interne funktioner i filteret af typen MS_VOID_SOURCE. Dets opgave er at hente hukommelse fra bunken. Andre filtre skal returnere det dertil. Det vil sige, at vi opdager, at der er en lækage.
For at bestemme, hvor på konvejeren inaktiviteten, der førte til hukommelseslækagen, fandt sted, foreslås det at introducere et ekstra filter, som blot flytter meddelelser fra indgangen til udgangen, men samtidig skaber en ikke-let, i en normal "tung" kopi af den indgående meddelelse, hvorefter den fuldstændigt fjerner meddelelsen, der kom ind. Vi kalder dette filter en isolator. Da vi antager, at filteret er simpelt, er lækage i det udelukket. En anden positiv egenskab er, at hvis vi tilføjer det et hvilket som helst sted i vores graf, vil det på ingen måde påvirke systemets funktion. Vi vil repræsentere isolatorfilteret som en cirkel med dobbelt kontur.
Vi aktiverer isolatoren straks efter filteret voidsourse:
![]()
Vi kører programmet igen med analysatoren og ser, at analysatoren denne gang placerer skylden på isolatoren. For det er nu den, der skaber datablokke, som senere går tabt af en ukendt uforsigtig filter (eller filtre). Næste skridt er at flytte isolatoren et filter til højre i kæden og igen starte analysen. Således, skridt for skridt bevæger vi isolatoren til højre, og vi får en situation, hvor antallet af "lækede" hukommelsesmængder i den næste rapport fra analysatoren vil falde. Det betyder, at isolatoren i dette skridt befandt sig i kæden lige efter det problematiske filter. Hvis det "dårlige" filter kun var én, ville lækagen forsvinde helt. På den måde har vi lokaliseret det problematiske filter (eller én af flere). Når vi har "repareret" filteret, kan vi fortsætte med at bevæge isolatoren til højre i kæden, indtil vi har overvundet hukommelseslækagen.
Implementeringen af isolatorfilteret
Implementeringen af isolatoren ligner en almindelig filter. Headerfil:
/* Файл 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
Selve filteret:
/* Файл 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)
Metode til at erstatte hukommelsesstyringsfunktioner
For mere detaljerede undersøgelser er der i mediastreameren mulighed for at erstatte hukommelsesadgangsfunktionerne med dine egne, som, udover deres primære funktion, vil registrere "Hvem, hvor og hvorfor". Tre funktioner erstattes. Dette gøres på følgende måde:
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);Denne mulighed er nyttig, når analysatoren bremser filterets ydeevne så meget, at det forstyrrer systemets funktion, hvori vores skema er implementeret. I en sådan situation er man nødt til at opgive analysatoren og bruge erstatningen af hukommelsesfunktioner.
Vi har gennemgået handlingsalgoritmen for en simpel graf uden forgreninger. Men denne tilgang kan også anvendes til andre tilfælde, selvfølgelig med mere komplekse forhold, men idéen vil forblive den samme.
I den næste artikel vil vi se på vurderingen af belastningen på tickeren og metoder til at håndtere overdreven beregningsbelastning i mediastreameren.
Kilde: habr.com
