[erlang-questions] Troubling gen_tcp.send/3 performance

Fred Hebert mononcqc@REDACTED
Mon Dec 5 03:13:56 CET 2016


On 12/04, Matthew Shapiro wrote:
>Yeah I think I can safely say this is something to do with how VLC is
>functioning.  Even remotely I'm getting stuck in send, though not as bad,
>and looking at what's going on in the difference between VLC and Ffmpeg I
>believe my timestamps are being unexpected and both are reacting weirdly
>too it, with VLC trying to slow the feed down for whatever reason and that
>may be causing it to be slow to do a TCP read.  Shot in the dark but at
>least I know now that it's not an BEAM issue (and my time on this hasn't
>been to waste).
>
>So sorry for the thread when Erlang/Beam isn't at fault in the end :)
>

It is and it is not. One of the interesting things with the TCP stack is 
that it sends a signal through ports, and if the send queue is full, the 
whole thing blocks; it however blocks with live data in there, and 
there's no good way to give it up.

The reason is blocks that the TCP work is done over a port program, 
meaning message passing takes place:

    Erlang Process               TCP port program
         |                               |
         |----------- Data ------------->|
         |                               |
         |<--- {inet_reply,S,Status} ----|

This is roughly what takes place within the thing. This is done through 
calling erlang:port_command/2-3. The thing though is that if the buffer 
of the port program is full, you get descheduled until there's place.

There's a cheat though, one we at Heroku have used in Logplex. The trick 
is to bypass the stack in critical 'must never block' circumstances and 
call port_command yourself, but by passing in the option list 
'[nosuspend]'.

When you do this and the port program representing the TCP socket is 
busy, you'll get a 'false' return value indicating it failed because it 
was full (you get 'true' when it works):

    case erlang:port_command(Socket, Data, [nosuspend]) of
        false ->
            handle_busy(...);
        true ->
            receive
                {inet_reply, Socket, Status} -> % Status == send return
                    Status
            end
    end

This will let you do things like figure out the consumer side (in this 
case VLC) is too slow, and possibly give up sending a few frames and 
send newer ones later.

Control flow is fun!

Regards,
Fred.



More information about the erlang-questions mailing list