-
Задача следующая - некий спецдевайс передает по сети по протоколу UDP данные (видеопоток), порядка 11 мегабайт в секунду, пакет 770 байт - строка. Прием занимает очень значительную часть процессорного времени (применяется WSA-функции + RecvFrom, также пробовал использовать Indy и ICS), причем, если программа занимается еще чем-то, то пакеты, приходящие в этот момент, теряются. Например, если после приема кадра отрисовывать его, то несколько первых строк следующего кадра не принимаются.
Можно ли предпринять какие-то меры к улучшению ситуации? C сетями ранее не работал, так что могут помочь любые идеи.
-
собирать на отдельном сервере. поднять приоритеты получалке. настучать в тыкву авторам передавалки.
%-)
-
> если программа занимается еще чем-то
Сделай так чтобы она занималась только этим и ничем более
-
т.е., такое потребление ресурсов и потери пакетов - это нормальная ситуация?
-
> MBo (31.01.2008 07:26:00) [0]
Для такого потока нужен уже 1 гигабит, 11 мегабайт в секунду можно передать только в тепличных условиях.
-
>[3] MBo ©(31.01.08 09:11) для UDP — да. оно ж лежит в буфере только пока новое не притащат. да и не факт, что старое дойдёт. потеряется по дороге — и всё.
-
>11 мегабайт в секунду можно передать только в тепличных условиях судя по снифферу, в стомегабитной локалке до машины пакеты доходят все, да и если программа не занимается обсчетом/отрисовкой, то она их все принимает.
>оно ж лежит в буфере насчет размера буфера я пока не разобрался. В ICS и в Indy можно выставить размер буфера, который, как я понимаю, отвечает за максимальный размер пакета. А есть ли еще какие-то возможности указать сетевой подсистеме винды, чтобы побольше непрочитанных еще пакетов накапливалось?
-
>[6] MBo ©(31.01.08 09:43) а максимальный размер пакета, афаир, всё равно 64к. и очереди в UDP нет. не успал поймать — кранты тому, что лежало.
-
> MBo © (31.01.08 09:43) [6]
см. SetSockOpt(..SO_RCVBUF..)
Но это не спасет, если выборка из буфера стабильно медленней, чем запись в буфер.
-
> MBo (31.01.2008 09:43:06) [6]
судя по снифферу, в стомегабитной локалке до машины пакеты доходят все, да и если программа не занимается обсчетом/отрисовкой, то она их все принимает.
Этого не должно быть, что то неправильно, в программе конечно. Но я бы все таки задумался 11 мегабайт это на пределе возможностей 100 мб сети. Не каждая машина и сетевая карта в состоянии обеспечить. А для процессора это ничто, доли процента.
Но сам протокол UDP не обеспечивает гарантированой доставки. Размер пакета надо снижать до минимума и делать буферизации, часть буферизации делает система.
-
> MBo ©
Выноси транспортную логику в отдельный поток, повысь ему приоритет до разумных пределов.
-
>[10] Сергей М. ©(31.01.08 09:53) так нечестно, я это уже посоветовал! %-)
-
да, прием ведется в отдельном потоке, повышение приоритета частично помогает, но полностью проблему не снимает.
-
>[12] MBo ©(31.01.08 10:11) ну, ты учти, что поток не может быть особо приоритетней приложения. подними ещё приоритет самому приложению. другим потокам приоритет поспускай.
а лучше таки накапливай где-то на отдельном сервере и с него уже забирай. и на гигабитке, желательно.
-
> лучше таки накапливай где-то на отдельном сервере и с него уже забирай. и на гигабитке, желательно
Увы, не получится - в реальности будут полевые условия, железка - кабель - ноутбук.
-
> MBo © (31.01.08 10:37) [14]
Ты бы код своего транспортного алгоритма показал ..
-
>[15] Сергей М. ©(31.01.08 10:54) я подозреваю, что ничего там такого нет. тупо сетка успевает впритык.
однако ж глянуть не помешает в любом случае, да.
-
инициализация в конструкторе потока
if WSAStartup($0202, WSAD) <> 0 then
Exception.Create('WSAStartup Error');
Ud := Socket(AF_INET, Sock_Dgram, 0);
if INVALID_SOCKET = Ud then
Exception.Create('Socket Create Error');
Addr.sin_family := PF_Inet;
Addr.sin_addr.S_addr := Inet_Addr('0.0.0.0');
Addr.sin_port := HtoNS(FPort);
FillChar(Addr.sin_zero, SizeOf(Addr.sin_zero), 0);
if Bind(UD, Addr, SizeOf(Addr)) = Socket_Error then
Exception.Create('Socket Bind Error');
поточная функция
Len := 770;
while not Terminated do begin
FromLen := SizeOf(TSockAddr);
WSResult := RecvFrom(UD, LineBuf, Len, 0, Addr, FromLen);
Wrd := Swap(PWord(@LineBuf[0])^);
LineNum := Wrd and $3FF;
CadrNum := (Wrd shr 12) and 1;
if LineNum = 0 then begin
Inc(FrameCount);
PostMessage(Form3.Handle, WM_USER, FrameCount and $F, CadrNum xor 1);
end;
Move(LineBuf[2], Buf[CadrNum, LineNum, 0], Wdt);
end;
Buf - 2 массива, один заполняется, предыдущий отрисовывается
в обработчике WM_USER отрисовка
SetDiBitsToDevice(Canvas.Handle, 0, 0, Wdt, Hgt, 0, 0, 0, Hgt,
@Buf[M.LParam], PBitmapInfo(@Info)^, DIB_RGB_COLORS);
-
у-у-у. PostMessage… да ещё и SetDiBits… есть (ничем не обоснованое, правда) мнение, что: эту фигню надо тихонько совать в буфер моска сразу по получении. раз. наладить frame skiping, если не успевает отрисоваться, а в буферг гадит. рисовать директиксом на оверлеях (тут, возможно, вылезут тормоза с преобразованием цветовых пространств, искать карты, оверлеи на которых потянут rgb).
да, это: присылает, как я понял, не твоя программа, так что вправить моск именно ей никак?
-
>PostMessage… да ещё и SetDiBits Отрисовку, временно забыв об опасностях обращения к основному окну из доп. потока, можно вести и прямо из потока, ничего принципиально это не меняет. SetDiBitsToDevice - оказался в данном случае наименее затратный способ из испытанных (альтернатива, например - формирование битмапа, происходит дольше)
насчет DirectX - еще не прорабатывал, побоялся затрат времени на преобразование цветов.
-
> один заполняется, предыдущий отрисовывается
А если отрисовка медленней заполнения ? Подумай ...
-
> MBo (31.01.2008 14:32:19) [19]
Для испытания временно отключи отрисовку.
-
>А если отрисовка медленней заполнения ? тогда бы я ставил в очередь и отрисовывал бы только часть кадров.
В данном случае хватает двух буферов. 14400 пакетов в секунду, 0.07 мс один пакет Отрисовка сначала у меня происходила более сложно, и занимала 3-4 мс, это по времени порядка 60 пакетов, около 40 КБ данных. Визуально - если критерий отрисовки - приход нулевой строки, то в районе 20-50 строк было много выпавших. Сейчас отрисовка быстрее, и артефактов на моей машине (Athlon 3500) практически нет, но на машинах вдвое медленнее - все-таки появляются.
Собственно, интересно - так и должна машина нагружаться при приеме из сети потока такой интенсивности, или руки нужно выпрямлять?
(С цифрового видеограббера без проблем принимаю, достаточно сложным образом обрабатываю и отрисовываю втрое-вчетверо больший поток)
-
> MBo (31.01.2008 15:47:22) [22]
100 мегабит это ничто для процессора, проблема не в количестве, а в другом месте.
-
>а в другом месте. Вот и хочется понять, что это за место ;)
-
> MBo (31.01.2008 18:31:24) [24]
Э, ну ты даешь, программа то у тебя, задача сложная, явно не для форума, да и сеть у тебя работает за пределами возможностей. 11 мб не любая сеть обеспечит, а еще твоя обработка. Так что придется тебе самому грызть.
-
> MBo ©
А что за девайс такой? IP камера? Просто знакомая мне тематика.
-
> Собственно, интересно - так и должна машина нагружаться > при приеме из сети потока такой интенсивности, или руки > нужно выпрямлять?
Нет, не должна. Вся нагрузка возникает, если в принимаемом потоке надо. что то искать, выцеплять, декодировать. Отрисовка это тоже мелочи.
-
procedure x;
type
TLineRec=packed record
Info:word;
Data:array[0..192*4-1] of char;
end;
var
Bitmap:TBitmap;
Line:TLineRec;
FromLen,LineNum,CadrNum,Wrd:integer;
WSResult:integer;
UD:TSocket;
Addr:TSockAddr;
begin
Bitmap:=TBitmap.Create;
try
Bitmap.PixelFormat:=pf32bit;
Bitmap.Width:=192;
Bitmap.Height:=192;
while not Terminated do begin
FromLen := SizeOf(TSockAddr);
WSResult := RecvFrom(UD, Line, SizeOf(Line), 0, Addr, FromLen);
if WSResult<>SizeOf(Line) then continue;
Wrd := Swap(Line.Info);
LineNum:= Wrd and $3FF;
if (LineNum>-1) and (LineNum<Bitmap.Height) then
Move(Line.Data,Bitmap.ScanLine[LineNum]^,SizeOf(Line.Data));
if LineNum = Bitmap.Height-1 then
begin
Inc(FrameCount);
FCanvas.Lock;
try
FCanvas.Draw(0,0,Bitmap);
finally
FCanvas.Unlock;
end;
end;
end;
finally
Bitmap.Free;
end;
end;
-
2 MBo ©
WSARecv c кучей буферов сразу под весь кадр не пробовал? Те буфер один, но разбит на несколько, по числу строк.
Синхронизацию сообщениями я бы выкинул. Как и многопоточность, скоре всего, использовал бы асинхронный режим. В любом случае, синхронизация здесь - лишние потери, имхо. Два буфера, один рисуется, второй принимается. Буферы переключает "приемник", а по WM_PAINT выводится "рисуемый", независимо от того, было ли обновление.
Как гипотеза - может вывод через DirectX побыстрее будет. Не знаю, не спец.
-
если картинка боле менее статичная, то потеря небольшого колва, каждый раз разных, строк не должна вызывать видимых артефактов... если конечно старый кадр не затирать "чернухой", а оставлять отображая новый "поверх" старого
|