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:
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); } } } }
The client.pl is:
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";

In reply to I know two prints are slower than one, but 200x slower?!?!? by rsmah

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.