[erlang-questions] RE: TCP performance

Chris Hicks silent_vendetta@REDACTED
Fri Jun 18 22:21:00 CEST 2010


I hadn't originally wanted it to be recursive, I just spawned a new process once that one was finished with its thing and let it die. I changed the accept function so that no matter what it recursed, to see if there was an improvement in using a pool of acceptors instead of constantly spawning a new one:


accept(Server, LSock) ->
case gen_tcp:accept(LSock) of
{ok, Socket} ->
gen_server:cast(gateway_verifier,{connected, Socket}),
accept(Server, LSock);{error, closed} ->
accept(Server, LSock)
 end.


And changed handle_cast so that it only spawned a new process if one of the ones it created crashed:

handle_cast(Request, State) ->
case Request of {accepted, _Pid} ->
{noreply, State};
{closed, _Pid} ->
{noreply, State};
{'EXIT', _From, _Reason} ->
{noreply, create_acceptor(1, State)};
_Other ->
{noreply, State}
end.


Changing to that method over my original one seems to drop the time down by around .1-.2 seconds on average so not much of an improvement. I had a pool of 10 acceptors. I'm running a quad-core 2.4 GHZ machine with 4G of ram and the total CPU time hovers around 40% on average with spikes up to around 50% when running this. One of the cores is much busier, around 75%, than the others but still isn't maxed out. I know I have the cpu cycles to burn so something is keeping it slower. Of course maybe my test case is too naive? I'm basically just bombarding the tcp connection from a single process as I figured it would give me a clear enough indication of how long it would take to handle the connections. Should I change the test code below?

gateway(0) -> ok;
gateway(Num) ->
        gen_tcp:connect('localhost', 54637, [binary, {packet, 0}, {reuseaddr, true}]),

        gateway(Num-1).----------------------------------------
> Date: Fri, 18 Jun 2010 16:13:41 +0400
> From: rumata-estor@REDACTED
> To: erlang-questions@REDACTED
> Subject: Re: [erlang-questions] RE: TCP performance
>
> accept/2 is not tail recursive, so it accepts only one connection.
>
> accept(Server, LSock) ->
> case gen_tcp:accept(LSock) of
> {ok, Socket} ->
> gen_server:cast(Server, {accepted, self()}),
> gen_server:cast(gateway_verifier, {connected, Socket}),
> accept(Server, LSock); %%<-------------
> {error, closed} ->
> gen_server:cast(Server, {closed, self()})
> end
>
>
> Dmitry Belyaev
>
>
> On 06/18/2010 05:56 AM, Chris Hicks wrote:
>>
>> I'm under the impression that hotmail and the erlang mailing list do not get along. I'll try to reformat the previous message one more time in a different way and if it doesn't work I guess I'm just going to have to open a second account elsewhere simply for the mailing list...
>>
>> Second attempt:
>>
>> In designing the TCP server, which will handle all communication, for my project I based my design off of a couple of different tutorials, modified to suit my own needs. What I have is a gen_server that spawns a new process which waits for a connection on the single port of communication, when a connection is accepted this process tells the gen_server that it needs to spawn a new process and then it sends the socket off to another gen_server where the data will be grabbed and processed.
>>
>> As far as I understand it once the socket connection has been accepted there should be no reason why another connection can't be established on that port immediately following. In other words the system doesn't care if the data has been received or not, when it comes to allowing another connection to take place, only that the first connection has been made. Is that true?
>>
>> I ask because I ran a very simple test against the server which simply asked for a connection and then immediately closed it. Everything seems to work flawlessly except that making 1000 connections takes about 3.5 seconds and for my purposes that is a bit too long. The code for the server is below, and hopefully with a tip someone gave me earlier it will be formatted correctly:
>>
>>
>> init(_Args) ->
>> process_flag(trap_exit, true),
>> case gen_tcp:listen(54637, [binary, {packet, 0}, {reuseaddr, true}]) of
>> {ok, LSock} ->
>> State = #state{lsock = LSock},
>> {ok, accept(State)};
>> {error, Reason} ->
>> {stop, Reason}
>> end.
>>
>>
>> accept(State = #state{lsock=LSock}) ->
>> proc_lib:spawn(?MODULE, accept, [self(), LSock]),
>> State.
>>
>>
>> accept(Server, LSock) ->
>> case gen_tcp:accept(LSock) of
>> {ok, Socket} ->
>> gen_server:cast(Server, {accepted, self()}),
>> gen_server:cast(gateway_verifier, {connected, Socket});
>> {error, closed} ->
>> gen_server:cast(Server, {closed, self()})
>> end.
>>
>>
>> This is the handle_cast callback for the gateway_verifier gen_server:
>>
>>
>>
>> handle_cast(Request, State) ->
>> case Request of
>> {connected, Socket} ->
>> {noreply, recv({State, Socket})};
>> {'EXIT', _From, _Reason} ->
>> {noreply, State};
>> _Other ->
>> {noreply, State}
>> end.
>>
>>
>> And these are the functions which will start the processing of the data sent along the socket:
>>
>>
>> recv({State, Socket}) ->
>> proc_lib:spawn(?MODULE, recv, [self(), Socket]),
>> State.
>>
>>
>> recv(_Server, Sock) ->
>> case gen_tcp:recv(Sock) of
>> {ok, _Packet} ->
>> %% Do some fancy stuff here
>> ok;
>> {error, Reason} -> {stop, Reason}
>> end.
>>
>>
>>
>> This is my first real attempt at this so I wouldn't be surprised if there were some glaring inefficiencies in there. Any thoughts on how to increase the speed of accepting incoming connections?
>> ----------------------------------------
>>
>>> From: silent_vendetta@REDACTED
>>> To: erlang-questions@REDACTED
>>> Subject: TCP performance
>>> Date: Thu, 17 Jun 2010 18:41:22 -0700
>>>
>>>
>>> In designing the TCP server, which will handle all communication, for my project I based my design off of a couple of different tutorials, modified to suit my own needs. What I have is a gen_server that spawns a new process which waits for a connection on the single port of communication, when a connection is accepted this process tells the gen_server that it needs to spawn a new process and then it sends the socket off to another gen_server where the data will be grabbed and processed.
>>> As far as I understand it once the socket connection has been accepted there should be no reason why another connection can't be established on that port immediately following. In other words the system doesn't care if the data has been received or not, when it comes to allowing another connection to take place, only that the first connection has been made. Is that true?
>>> I ask because I ran a very simple test against the server which simply asked for a connection and then immediately closed it. Everything seems to work flawlessly except that making 1000 connections takes about 3.5 seconds and for my purposes that is a bit too long. The code for the server is below, and hopefully with a tip someone gave me earlier it will be formatted correctly:
>>> init(_Args) -> process_flag(trap_exit, true), case gen_tcp:listen(54637, [binary, {packet, 0}, {reuseaddr, true}]) of {ok, LSock} -> State = #state{lsock = LSock}, {ok, accept(State)}; {error, Reason} -> {stop, Reason} end. accept(State = #state{lsock=LSock}) -> proc_lib:spawn(?MODULE, accept, [self(), LSock]), State. accept(Server, LSock) -> case gen_tcp:accept(LSock) of {ok, Socket} -> gen_server:cast(Server, {accepted, self()}), gen_server:cast(gateway_verifier, {connected, Socket}); {error, closed} -> gen_server:cast(Server, {closed, self()}) end.
>>> This is the handle_cast callback for the gateway_verifier gen_server:
>>> handle_cast(Request, State) -> case Request of {connected, Socket} -> {noreply, recv({State, Socket})}; {'EXIT', _From, _Reason} -> {noreply, State}; _Other -> {noreply, State} end.
>>> And these are the functions which will start the processing of the data sent along the socket:
>>> recv({State, Socket}) -> proc_lib:spawn(?MODULE, recv, [self(), Socket]), State. recv(_Server, Sock) -> case gen_tcp:recv(Sock) of {ok, _Packet} -> %% Do some fancy stuff here ok; {error, Reason} -> {stop, Reason} end.
>>>
>>>
>>> This is my first real attempt at this so I wouldn't be surprised if there were some glaring inefficiencies in there. Any thoughts on how to increase the speed of accepting incoming connections?
>>> _________________________________________________________________
>>> Hotmail is redefining busy with tools for the New Busy. Get more from your inbox.
>>> http://www.windowslive.com/campaign/thenewbusy?ocid=PID28326::T:WLMTAGL:ON:WL:en-US:WM_HMP:042010_2
>>>
>>
>> _________________________________________________________________
>> Hotmail has tools for the New Busy. Search, chat and e-mail from your inbox.
>> http://www.windowslive.com/campaign/thenewbusy?ocid=PID28326::T:WLMTAGL:ON:WL:en-US:WM_HMP:042010_1
>> ________________________________________________________________
>> erlang-questions (at) erlang.org mailing list.
>> See http://www.erlang.org/faq.html
>> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>>
>>
>>
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>
 		 	   		  
_________________________________________________________________
The New Busy think 9 to 5 is a cute idea. Combine multiple calendars with Hotmail. 
http://www.windowslive.com/campaign/thenewbusy?tile=multicalendar&ocid=PID28326::T:WLMTAGL:ON:WL:en-US:WM_HMP:042010_5


More information about the erlang-questions mailing list