Explorando o motor de VoIP Mediastreamer2. Parte 12

O material do artigo está tirado do meu canle zen.

Explorando o motor de VoIP Mediastreamer2. Parte 12

No último Artigo, Prometín considerar a cuestión de avaliar a carga do ticker e as formas de combater a carga de computación excesiva no streamer multimedia. Pero decidín que sería máis lóxico cubrir os problemas de depuración de filtros artesanais relacionados co movemento de datos e só entón considerar cuestións de optimización do rendemento.

Depuración de filtros artesanais

Despois de analizar o mecanismo para mover datos nun streamer multimedia no artigo anterior, sería lóxico falar dos perigos que se agochan nel. Unha das características do principio de "fluxo de datos" é que a memoria se asigna a partir do montón en filtros que están situados na orixe do fluxo de datos, e a memoria é liberada e devólvese ao montón mediante filtros situados ao final do fluxo. camiño. Ademais, a creación de novos datos e a súa destrución pode ocorrer nalgún lugar intermedio. En xeral, a liberación da memoria realízase mediante un filtro diferente ao que creou o bloque de datos.

Desde o punto de vista da vixilancia transparente da memoria, sería razoable que o filtro, ao recibir un bloque de entrada, o destrúa inmediatamente despois do procesamento, liberando a memoria e saia un bloque recén creado con datos de saída. Neste caso, rastrexaríase facilmente unha fuga de memoria no filtro: se o analizador detecta unha fuga no filtro, o seguinte filtro non destrúe correctamente os bloques entrantes e hai un erro nel. Pero desde o punto de vista de manter un alto rendemento, este enfoque para traballar con bloques de datos non é produtivo: leva a un gran número de operacións para asignar/liberar memoria para bloques de datos sen ningunha saída útil.

Por este motivo, os filtros media streamer, para non ralentizar o procesamento de datos, utilizan funcións que crean copias sinxelas ao copiar mensaxes (falamos delas no artigo anterior). Estas funcións só crean unha nova instancia da cabeceira da mensaxe "anexando" a ela un bloque de datos da mensaxe "vella" que se está copiando. Como resultado, dous encabezados están unidos a un bloque de datos e o contador de referencia do bloque de datos increméntase. Pero parecerán dúas mensaxes. Pode haber máis mensaxes cun bloque de datos "socializado", por exemplo, o filtro MS_TEE xera unha ducia de tales copias lixeiras á vez, distribuíndoas entre as súas saídas. Se todos os filtros da cadea funcionan correctamente, ao final da canalización este contador de referencia debería chegar a cero e chamarase á función de liberación de memoria: ms_free(). Se a chamada non se produce, esta peza de memoria xa non se devolverá ao montón, é dicir. vai "filtrar". O prezo por usar copias lixeiras é a perda da capacidade de determinar facilmente (como sería o caso das copias habituais) que filtro gráfico está a perder memoria.

Dado que os desenvolvedores de medios de transmisión son responsables de atopar fugas de memoria nos filtros nativos, é probable que non teñas que depuralas. Pero co teu filtro artesanal, es o saltón da túa propia felicidade e o tempo que pases buscando filtracións no teu código dependerá da túa precisión. Para reducir o tempo de depuración, debemos analizar as técnicas de detección de fugas ao desenvolver filtros. Ademais, pode ocorrer que a fuga se manifeste só cando o filtro se aplique nun sistema real, onde o número de "sospeitosos" pode ser enorme e o tempo de depuración limitado.

Como se manifesta unha fuga de memoria?

É lóxico asumir que na saída do programa arriba mostrará a porcentaxe crecente de memoria ocupada pola súa aplicación.

A manifestación externa será que nalgún momento o sistema comezará a responder lentamente ao movemento do rato e volverá debuxar lentamente a pantalla. Tamén é posible que o rexistro do sistema creza, ocupando espazo no disco duro. Neste caso, a súa aplicación comezará a comportarse de forma estraña, non responderá aos comandos, non poderá abrir un ficheiro, etc.

Para detectar a aparición dunha fuga, utilizaremos un analizador de memoria (en diante analizador). Podería ser valgrind (ben artigo sobre iso) ou incorporado ao compilador gcc MemorySanitizer ou calquera outra cousa. Se o analizador mostra que se produce unha fuga nun dos filtros gráficos, isto significa que é hora de aplicar un dos métodos descritos a continuación.

Método dos Tres Piñeiros

Como se mencionou anteriormente, se hai unha fuga de memoria, o analizador apuntará ao filtro que solicitou a asignación de memoria do montón. Pero non sinalará o filtro que "esqueceu" de devolvelo, que, de feito, é o culpable. Así, o analizador só pode confirmar os nosos medos, pero non indicar a súa raíz.

Para coñecer a localización do filtro "malo" no gráfico, pode ir reducindo o gráfico ao número mínimo de nodos nos que o analizador aínda detecta unha fuga e localizar o filtro problemático nos tres piñeiros restantes.

Pero pode ocorrer que ao reducir o número de filtros no gráfico, perturbe o curso normal da interacción entre os filtros e outros elementos do seu sistema e a fuga xa non aparecerá. Neste caso, terás que traballar cun gráfico de tamaño completo e utilizar o enfoque que se describe a continuación.

Método illante deslizante

Para simplificar a presentación, utilizaremos un gráfico que consta dunha cadea de filtros. Ela móstrase na imaxe.

Explorando o motor de VoIP Mediastreamer2. Parte 12

Un gráfico regular no que, xunto cos filtros de transmisión multimedia xa preparados, se utilizan catro filtros artesanais F1...F4, de catro tipos diferentes, que fixeches hai tempo e que non tes dúbida da súa corrección. Non obstante, supoñamos que varios deles teñen fugas de memoria. Ao executar o noso programa para supervisar o analizador, decatámonos do seu informe de que un determinado filtro solicitou unha certa cantidade de memoria e non a devolveu ao montón N de veces. Podes adiviñar facilmente que haberá unha ligazón ás funcións de filtro interno do tipo MS_VOID_SOURCE. A súa tarefa é sacar a memoria do montón. Outros filtros deben devolvelo alí. Eses. detectaremos o feito da fuga.

Para determinar en que sección da canalización houbo inacción que provocou unha fuga de memoria, proponse introducir un filtro adicional que simplemente cambie as mensaxes de entrada á saída, pero ao mesmo tempo crea unha copia da mensaxe de entrada que non é clara. , senón un "pesado" normal, despois elimina por completo a mensaxe recibida na entrada. Chamaremos tal filtro un illante. Cremos que, dado que o filtro é sinxelo, non hai fugas nel. E unha propiedade positiva máis: se a engadimos en calquera lugar do noso gráfico, isto non afectará de ningún xeito o funcionamento do circuíto. Representaremos o filtro-illador en forma de círculo cun dobre circuíto.

Acendemos o illante inmediatamente despois do filtro Vodsource:
Explorando o motor de VoIP Mediastreamer2. Parte 12

Volvemos a executar o programa co analizador e vemos que esta vez o analizador culpará ao illante. Despois de todo, é el quen agora crea bloques de datos, que despois se perden por un filtro (ou filtros) descoñecidos. O seguinte paso é mover o illante ao longo da cadea cara á dereita, por un filtro, e comezar de novo a análise. Así, paso a paso movendo o illante cara á dereita, obteremos unha situación na que no seguinte informe do analizador diminuirá o número de bloques de memoria "filtrados". Isto significa que neste paso o illante estaba na cadea inmediatamente despois do filtro do problema. Se só houbese un filtro "malo", entón a fuga desaparecerá por completo. Así, localizamos o filtro problemático (ou un de varios). Unha vez "fixado" o filtro, podemos seguir movendo o illante cara á dereita ao longo da cadea ata que eliminemos completamente as fugas de memoria.

Implantación dun filtro illante

A implementación do illante parece un filtro normal. Ficheiro de cabeceira:

/* Файл 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

O propio filtro:

/* Файл 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)

Método para substituír funcións de xestión da memoria

Para unha investigación máis sutil, o streamer multimedia ofrece a posibilidade de substituír as funcións de acceso á memoria polas súas propias, que, ademais do traballo principal, gravará "Quen, onde e por que". Substitúense tres funcións. Isto faise do seguinte xeito:

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);

Esta función axuda nos casos nos que o analizador ralentiza tanto o funcionamento dos filtros que se interrompe o funcionamento do sistema no que está construído o noso circuíto. En tal situación, ten que abandonar o analizador e utilizar a substitución de funcións para traballar coa memoria.

Consideramos un algoritmo de accións para un gráfico sinxelo que non contén ramas. Pero este enfoque pódese aplicar a outros casos, por suposto con máis complexidade, pero a idea segue sendo a mesma.

No seguinte artigo, analizaremos o problema da estimación da carga do ticker e como xestionar a carga de computación excesiva no streamer multimedia.

Fonte: www.habr.com

Engadir un comentario