Вывучаем VoIP-рухавічок Mediastreamer2. Частка 12

Матэрыял артыкула ўзяты з майго дзэн-канала.

Вывучаем VoIP-рухавічок Mediastreamer2. Частка 12

У мінулым артыкуле, я абяцаў разгледзець пытанне ацэнкі нагрузкі на Біржавы сімвал і спосабы барацьбы з празмернай вылічальнай нагрузкай у медыястрымеры. Але вырашыў, што будзе больш лагічна асвятліць пытанні адладкі крафтавых фільтраў, звязаныя з перамяшчэннем дадзеных і ўжо потым разгледзець пытанні аптымізацыі прадукцыйнасці.

Адладка крафтавых фільтраў

Пасля таго, як мы ў папярэднім артыкуле разгледзелі механізм перамяшчэння дадзеных у медыястрымеры будзе лагічна пагаварыць аб небяспецы, якая хаваецца ў ім. Адна з асаблівасцяў прынцыпу "data flow" складаецца ў тым, што вылучэнне памяці з кучы адбываецца ў фільтрах, якія знаходзяцца ў вытокаў струменя дадзеных, а вызваленне памяці з вяртаннем у кучу робяць ужо фільтры, размешчаныя ў канцы шляху струменя. Акрамя гэтага, стварэнне новых дадзеных і іх знішчэнне можа адбывацца недзе ў прамежкавых кропках. Увогуле выпадку, вызваленне памяці выконвае не той фільтр, што стварыў блок дадзеных.

З пункту гледжання празрыстага маніторынгу за памяццю было б разумна, каб фільтр, атрымліваючы ўваходны блок, пасля апрацоўкі тут жа знішчаў яго з вызваленнем памяці, а на выйсце выстаўляў бы ізноў створаны блок з выходнымі дадзенымі. У гэтым выпадку ўцечка памяці ў фільтры лёгка б трасіравалася – калі аналізатар выявіў уцечку ў фільтры, то значыць наступны за ім фільтр не знішчае ўваходныя блокі належнай выявай і памылка ў ім. Але з пункту гледжання падтрымання высокай прадукцыйнасці, такі падыход да працы з блокамі дадзеных, не прадукцыйны - ён прыводзіць да вялікай колькасць аперацый па вылучэнні/вызваленню памяці пад блокі дадзеных без якога-небудзь карыснага выхлапу.

Па гэтай прычыне фільтры медыястрымера, каб не запавольваць апрацоўку дадзеных, пры капіяванні паведамленняў выкарыстоўваюць функцыі якія ствараюць лёгкія копіі (мы расказвалі пра іх у мінулым артыкуле). Гэтыя функцыі толькі ствараюць новы асобнік загалоўка паведамлення "прыфастрыгоўваючы" да яго блок дадзеных ад які капіюецца "старога" паведамленні. У выніку, да аднаго блоку дадзеных апыняюцца прывязанымі два загалоўка і выконваецца інкрымент лічыльніка спасылак у блоку дадзеных. Але выглядаць гэта будзе як два паведамленні. Паведамленняў з такім "абагульненым" блокам дадзеных можа быць і больш, так напрыклад фільтр MS_TEE спараджае адразу дзясятак такіх лёгкіх дзід, размяркоўваючы іх па сваіх вынахадах. Пры правільнай працы ўсіх фільтраў у ланцужку, да канца канвеера гэты лічыльнік спасылак павінен дасягнуць за нуль і будзе выкліканая функцыя вызвалення памяці: ms_free(). Калі выкліку не адбываецца, тое значыць гэты кавалак памяці ўжо не вернецца ў кучу, г.зн. ён "уцячэ". Адплатай за выкарыстанне лёгкіх дзід служыць страта магчымасці лёгка ўсталяваць (як гэта было б у выпадку выкарыстання звычайных дзід) у якім фільтры графа выцякае памяць.

Паколькі адказнасць за пошук уцечак памяці ў "родных" фільтрах ляжыць на распрацоўшчыках медыястрымера, то хутчэй за ўсё вам не давядзецца іх адладжваць. Але вось з вашым крафтавым фільтрам - вы самі конік свайго шчасця і ад вашай акуратнасці будзе залежаць час які вы праведзяце ў пошуках уцечак у вашым кодзе. Каб скараціць ваш час пакут з адладкай, мы павінны разгледзець прыёмы лакалізацыі ўцечак пры распрацоўцы фільтраў. Да таго ж, можа здарыцца так, уцечка выявіць сябе толькі пры ўжыванні фільтра ў рэальнай сістэме, дзе колькасць "падазроных" можа апынуцца велізарным, а час на адладку абмежаваным.

Як праяўляе сябе ўцечка памяці?

Лагічна меркаваць, што ў выснове праграмы топ будзе паказвацца нарастальны працэнт памяці, які займае вашым дадаткам.

Вонкавая праява будзе складацца ў тым, што ў нейкі момант сістэма стане запаволена рэагаваць на рух мышкі, павольна перамалёўваць экран. Магчыма таксама будзе расці сістэмны лог, з'ядаючы месца на цвёрдай кружэлцы. Пры гэтым ваша дадатак пачне паводзіць сябе дзіўна, не адказваць на каманды, не можа адкрыць файл і т.д.

Каб выявіць факт узнікнення ўцечкі будзем выкарыстоўваць аналізатар памяці (далей аналізатар). Гэта можа быць Вальгрынд (добрая артыкул аб ім) або убудаваны ў кампілятар НКУ Ачышчальнік памяці ці што-небудзь іншае. Калі аналізатар пакажа, што ўцечка адбываецца ў адным з фільтраў графа, тое гэта азначае што сітавіна ўжыць адзін са спосабаў апісаных ніжэй.

Метад трох соснаў

Як ужо было сказанае вышэй, пры ўцечцы памяці аналізатар пакажа на фільтр, які запытаў вылучэнне памяці з кучы. Але не пакажа на фільтр які "забыўся" яе вярнуць, які, уласна, і з'яўляецца вінаватым. Тым самым, аналізатар можа толькі пацвердзіць нашыя асцярогі, але не паказаць на іх корань.

Каб высветліць размяшчэнне "нядобрага" фільтра ў графе, можна пайсці шляхам скарачэння графа да мінімальнай колькасці вузлоў, пры якім аналізатар яшчэ выяўляе ўцечку і ў пакінутых трох хвоях лакалізаваць праблемны фільтр.

Але можа здарыцца так, што скарачаючы лік фільтраў у графе вы парушыце звычайны ход узаемадзеяння фільтраў з іншымі элементамі вашай сістэмы і ўцечка перастане выяўляцца. У гэтым выпадку давядзецца працаваць з поўнапамерным графам і выкарыстоўваць падыход, які выкладзены ніжэй.

Метад слізгальнага ізалятара

Для прастаты выкладу скарыстаемся графам які складаецца з аднаго ланцужкі фільтраў. Яна намалявана на малюнку.

Вывучаем VoIP-рухавічок Mediastreamer2. Частка 12

Звычайны граф, у якім нароўні з гатовымі фільтрамі медыястрымеру ўжытыя чатыры крафтавых фільтра F1…F4, чатырох розных тыпаў, якія вы зрабілі даўно і ў іх карэктнасці не сумняваецеся. Тым не менш выкажам здагадку, што ў некалькі з іх маецца ўцечка памяці. Запускаючы нашу праграму па наглядам аналізатара, з яго справаздачы мы даведаемся, што нейкі фільтр запытаў некаторую колькасць памяці і не вярнуў яго ў кучу N-ую колькасць разоў. Лёгка можна здагадацца, ці будзе спасылка на ўнутраныя функцыі фільтра тыпу MS_VOID_SOURCE. У яго задача такая - забіраць памяць з кучы. Вяртаць яе туды павінны іншыя фільтры. Г.зн. мы выявім факт уцечкі.

Каб вызначыць на якім участку канвеера адбылося бяздзеянне якое прывяло да ўцечкі памяці, прапануецца ўвесці дадатковы фільтр, які проста перакладае паведамленні са ўваходу на вынахад, але пры гэтым стварае не лёгкую, у звычайную "цяжкую" копію ўваходнага паведамлення, затым цалкам выдаляючы паведамленне, які паступіў на уваход. Будзем называць такі фільтр ізалятарам. Мяркуем, што паколькі фільтр просты, то ўцечка ў ім выключана. І яшчэ адна дадатная ўласцівасць — калі мы дадамо яго ў любое месца нашага графа, тое гэта ніяк не адаб'ецца на працы схемы. Будзем адлюстроўваць фільтр-ізалятар у выглядзе круга з падвойным контурам.

Уключаем ізалятар адразу пасля фільтра voidsourse:
Вывучаем VoIP-рухавічок Mediastreamer2. Частка 12

Зноў запускаем праграму з аналізатарам, і бачым, што ў гэты раз, аналізатар ускладзе віну на ізалятар. Бо гэта ён зараз стварае блокі дадзеных, якія потым губляюцца невядомым нядбайным фільтрам (ці фільтрамі). Наступным крокам зрушваем ізалятар па ланцужку направа, на адзін фільтр і зноў запускаем аналіз. Так, крок за крокам рухаючы ізалятар направа, мы атрымаем сітуацыю, калі ў чарговай справаздачы аналізатара колькасць "уцёклых" блокаў памяці паменшыцца. Гэта значыць, што на гэтым кроку ізалятар апынуўся ў ланцужку адразу пасьля праблемнага фільтра. Калі "дрэнны" фільтр быў адзін, то ўцечка і зусім знікне. Такім чынам мы лакалізавалі праблемны фільтр (ці адзін з некалькіх). "Паправіўшы" фільтр, мы можам працягнуць рухаць ізалятар направа па ланцужку да поўнай перамогі над уцечкамі памяці.

Рэалізацыя фільтра-ізалятара

Рэалізацыя ізалятара выглядае таксама як звычайны фільтр. Загалоўкавыя файл:

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

Сам фільтр:

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

Метад падмены функцый кіравання памяццю

Для больш тонкіх даследаванняў, у медыястрымеры прадугледжана магчымасць падмены функцый доступу да памяці на вашыя ўласныя, якія апроч асноўнай працы будуць фіксаваць "Хто, куды і навошта". Падмяняюцца тры функцыі. Гэта робіцца наступным чынам:

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

Такая магчымасць выбаўляе ў выпадках, калі аналізатар запавольвае працу фільтраў настолькі, што парушаецца праца сістэмы, у якую ўбудавана наша схема. У такой сітуацыі даводзіцца адмаўляцца ад аналізатара і выкарыстоўваць падмену функцый працы з памяццю.

Мы разгледзелі алгарытм дзеянняў для простага графа, які не змяшчае разгалінаванняў. Але гэты падыход можна прымяніць і для іншых выпадкаў, канешне з ускладненнем, але ідэя застанецца той жа.

У наступным артыкуле, мы разгледзім пытанне ацэнкі нагрузкі на Біржавы сімвал і спосабы барацьбы з празмернай вылічальнай нагрузкай у медыястрымеры.

Крыніца: habr.com

Дадаць каментар