Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby socket performance characteristics

I am performing repeated send/recv calls on TCP sockets in Ruby, and have found significant speed differences between two socket uses---simply put, reusing a socket is proving slower than continually closing and re-opening one.

The server is thus:

s = TCPServer.new( 4545 )
while( c = s.accept )
  while( m = c.gets )
    c.puts( m.chomp )
  end
end
s.close

It simply echoes the request back to the client.

Client 1 reconnects each time:

t1 = Time.now
1000.times{
  s = TCPSocket.new( '127.0.0.1', 4545 )
  s.puts( 'test' )
  s.gets
  s.close
}
puts "Reconnecting: #{Time.now - t1}s"

Client 2 holds its socket open:

t1 = Time.now
s = TCPSocket.new( '127.0.0.1', 4545 )
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) #Nagle
1000.times{
  s.puts( 'test' )
  s.gets
}
s.close
puts "Persistent: #{Time.now - t1}s"

The result of running this code is as below:

% ruby test_client.rb
Reconnecting: 0.233751849s
Persistent (w/Nagle): 79.925120196s
Persistent (NODELAY): 39.958955967s

It is my understanding that reusing a socket should save me time (not take 347 times longer!). I've tried calling IO#flush after writing to sockets, but this makes no difference. Disabling Nagle's algorithm helps to some extent but nowhere near enough.

What would cause the code above (running on Linux 3.8.5 and ruby 2.0.0) to execute with such a great discrepancy in speed, and how can I rectify this?

like image 616
Stephen Wattam Avatar asked May 27 '13 16:05

Stephen Wattam


1 Answers

Disabling Nagle's algorithm on both the client and server sockets fixes this:

s = TCPServer.new( 4545 )
while( c = s.accept )
  c.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
  while( m = c.gets )
    c.puts( m.chomp )
  end
end
s.close

Resulting in:

% ruby test_client.rb
Reconnect: 0.189858182s
Persistent: 0.030973398s

Hope this helps someone.

like image 149
Stephen Wattam Avatar answered Sep 23 '22 23:09

Stephen Wattam