------ extPart_000_05D6_01C4AF00.9A646CB0 Content-Type: text/plain; charset so-8859-1" Content-Transfer-Encoding: 7bit Hi, From: "Michael Gebhart" <mail / miketech.net> > I am just thinking about programming a serverapplication with Ruby. The > application should manage a lot of sockets and should also be > multithreaded. One year ago I have tried to develop a similar application > with php. The performance was absolutely bad. No, that was not only my > fault ;) My question is now: What about the performance of ruby? Is it > possible to write an application using sockets and threads with ruby, but > also with a good performance? Normally I am using C/C++ for these things. > Sure, Ruby will not be as far as C/C++. But the php-scripts were very very > slow. Has anyone experiences with this? I've just hacked up a TCP/IP throughput test of sorts, where you can spawn a server, and as many clients as you want (each client is a separate OS process.) The server and clients will blast data at each other in chunks of a specified byte size, for a specified number of seconds. The server prints some stats about the throughput as each client disconnects. Dunno if it will be useful to you. One thing of interest to me is that the chunk-size makes a huge difference. (I.e. printing 2048 bytes at a time is hugely faster than printing 64 bytes at a time. I expected some difference, of course, but not as much as I'm seeing.) With a server transmit chunk-size of 1024, and a client transmit chunk-size of 2048... (Arbitrary, just trying different values)... Servicing 100 clients, each of which connected for 60 seconds, I get: 80.045 seconds, MB in: 190.75 (2.38 MB/sec), MB out: 30.38 (0.38 MB/sec) I'm running win2k with a 1.33GHz athlon CPU. The elapsed time of 80 seconds is because it took awhile to start up all 100 clients (which each ran for 60 sec.) Leaving the server chunk-size at 1024 but changing the client size to 64, I get: 95.728 seconds, MB in: 38.51 (0.40 MB/sec), MB out: 12.15 (0.13 MB/sec) ...And I had trouble even starting 100 client processes this time... Well, the same kinds of chunk-size differences are apparent when running even one client... Will have to try this on Linux ... Anyway for whatever it's worth ... :) Regards, Bill ------ extPart_000_05D6_01C4AF00.9A646CB0 Content-Type: application/octet-stream; name cptest.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename cptest.rb" #!/usr/bin/env ruby require 'thread' require 'socket' # # TCP client/server throughput test # # Start server like: ruby tcptest.rb --server --chunk-bytes 1024 # Start client like: ruby tcptest.rb --run-seconds 10 --chunk-bytes 2048 # # --run-seconds applies only to client # --chunk-bytes is the length of the string "printed" to the peer # # THE SERVER: # The server waits for clients to connect, and creates two threads # per client. The read thread reads data transmitted by the client # as fast as possible, and the write thread transmits blocks of data # to the client (chunk-bytes in length) as fast as possible. # # When a client disconnects, some megabytes-per-sec in/out statistics # are printed. # # Server keeps running until you kill it, e.g. with ^C # # THE CLIENT: # The client connects to the server, and for run-seconds time, reads # and writes as much data from/to the server as possible. # Client transmits chunk-bytes size blocks of data to the server. # # On windows, can spawn a batch of client processes like, for ex: # ruby -e "10.times {system('start rubyw tcptest.rb --run-seconds 60 --chunk-bytes 2048')}" SERVER_PORT 2345 Thread.abort_on_exception alse $server_mode RGV.include? "--server" $xmit_chunk_size RGV.join(' ') /--chunk-bytes\s*(\d+)/ ? $1.to_i : 256 $run_seconds RGV.join(' ') /--run-seconds\s*(\d+)/ ? $1.to_i : 60 $xmit_dat ." * $xmit_chunk_size $total_bytes_in $total_bytes_out $server_start_time il # set when first client connects class ClientSession def initialize(client_sock) @client lient_sock @client_port lient_sock.peeraddr[1] @bytes_in @bytes_out @start_time ime.now puts "client #{@client_port} connected, servicing..." @read_th hread.new {background_read} @write_th hread.new {background_write} end def stop @read_th.kill @write_th.kill end def stats(bytes_in, bytes_out, elapsed_sec) in_mb ytes_in.to_f / 1024**2 out_mb ytes_out.to_f / 1024**2 "MB in: %2.2f (%2.2f MB/sec), MB out: %2.2f (%2.2f MB/sec)" % [in_mb, in_mb/elapsed_sec, out_mb, out_mb/elapsed_sec] end def background_read begin while line client.gets @bytes_in + ine.length end ensure @write_th.kill elapsed_sec ime.now - @start_time puts "client #{@client_port} disconnected after #{elapsed_sec} seconds, " + stats(@bytes_in, @bytes_out, elapsed_sec) Thread.exclusive { $total_bytes_in + bytes_in $total_bytes_out + bytes_out total_elapsed_sec ime.now - $server_start_time puts "cumulative totals: #{total_elapsed_sec} seconds, " + stats($total_bytes_in, $total_bytes_out, total_elapsed_sec) } end end def background_write loop do @client.puts $xmit_dat @bytes_out + xmit_dat.length + 1 # +1 for linefeed end end end def run_server client_sessions ] abort_th hread.new { loop {sleep(1)} } # so will respond to ^C on win32 server CPServer.new('localhost', SERVER_PORT) begin while client erver.accept $server_start_time ime.now unless $server_start_time client_sessions << ClientSession.new(client) end ensure client_sessions.each {|cl| cl.stop } server.close abort_th.kill end end def run_client conn CPSocket.new('localhost', SERVER_PORT) end_time ime.now + $run_seconds write_th hread.new { conn.puts $xmit_dat while Time.now < end_time } read_th hread.new { loop {conn.gets} } write_th.join read_th.kill conn.close end $server_mode ? run_server : run_client ------ extPart_000_05D6_01C4AF00.9A646CB0--