rsmah has asked for the wisdom of the Perl Monks concerning the following question:
I was doing a bit of experimenting with writing a socket server and found some weird behavior that I just don't understand.
I have a simple forking server that echo's data back to the client. Connections are persistent, multiple requests can take place over a single connection. The client simply opens a connection, sends a line of text, reads lines back until a period -- repeats this many times -- and then exits.
When server.pl prints its output to the client using 1 print statement, it's lickety split fast. When it uses 2 or more print statements, it's dead slow. Not just a little slower, over 100x slower! I can't understand why.
Let's run a test using a single print statement in the server (the arg 1 means use 1 print statement):
$ perl server-fork.pl 1 & $ time perl client.pl 1000 > /dev/null real 0m0.163s user 0m0.093s sys 0m0.021s
And now using 2 print statements:
$ perl server-fork.pl 0 & $ time perl client.pl 1000 > /dev/null real 0m40.018s user 0m0.052s sys 0m0.015s
That's about 245 times slower! Now, I'd expect it to be a bit slower, and I could even accept 2x or 5x slower, but 200x times slower?!?! This just makes no sense to me. Can someone who understands how the perl stdio layer interacts with TCP sockets on Linux explain this?
A few notes that might help:
And finally, here's my stupid simple code:
The server.pl is:The client.pl is:use strict; use IO::Select; use IO::Socket::INET; $SIG{CHLD} = 'IGNORE'; my $one_print = shift @ARGV || 0; my $listener = IO::Socket::INET->new(Listen => 1, LocalPort => 8080); my $select = IO::Select->new($listener); while( my @ready = $select->can_read ){ foreach( @ready ){ if( $_ == $listener ){ my $sock = $listener->accept; my $pid = fork; if( defined $pid && $pid == 0 ){ while( 1 ){ my $line = <$sock>; last unless defined $line; if( $line =~ /^QUIT/ ){ print $sock "Goodbye\n"; $sock->close; last; }elsif( $one_print ){ print $sock $line, ".\n"; }else{ print $sock $line; print $sock ".\n"; } } # while read loop exit(0); } } } }
use strict; use IO::Socket::INET; my $trials = shift @ARGV; my $sock = IO::Socket::INET->new(PeerHost => 'localhost', PeerPort => +8080); for(1..$trials){ my $tm = time; print $sock "Hello\n"; while(my $line = <$sock>){ last if $line eq ".\n"; print $line;; } } print $sock "QUIT\n";
|
|---|