Subject: fork speed
From: [email-masked] (Tim Newsham)

29 Oct 1997 18:12:14 -0800
Message-ID: <[email-masked]>
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
To: [email-masked]
Cc: [email-masked]

Hi,

   I've been doing some profiling of the fork() call in cygwin.dll
to find out why it is so slow and to see what could be done to speed
it up.  I am using a pentium 133mhz with 32 megs of ram.  My observations
are:

From fork() entry to parent exit averages about 50 msec.
from fork entry to child exit averages about 75 msec.

75 msec is a lot of computer time.  I broke down the time a
bit.  The biggest events are:

     enter fork
     CreateProcess  ----->
     do some copying
     resume child   ----->           child enters crt0
     wait for child                  child longjmps to fork code
                    <-----           signal parent to continue
     more copying                    wait for parent
     return                          init uinfo
                                     init socket
                                     return

the parent takes about 10msec doing the initial steups until waiting
for the child.  The child enters crt0 about 45msec after the
fork started and quickly longjmps to the fork code and signals
the parent to continue (at about 47msec the parent continues).
The parent then finishes its work at about 50msec.  The child
side gets signalled by the parent at around 50msec.  It then
initializes uinfo until 65msec after fork, and initializes sockets
until about 75msec after the fork.  Then it returns at about
75msec after the fork.

I have yet to look into where the time goes when doing the
CreateProcess.  This seems to be taking up about 35msec, roughly
half the fork time, between when CreateProcess is called and
the child actually starts running.

I did look into the child initialization functions to init
uinfo and init sockets a bit though.  

The init uinfo function reads in /etc/passwd and /etc/group and 
figures out what the user id is for the current process.  This
seems a bit wasteful to me.  Is it ever the case that a fork()
operation is going to change the uid and username of a user?
I took out the call to the uinfo init function from the fork
call and changed the passwd and group functions to do initialization
(reading /etc/passwd and /etc/group) on first lookup (ie.
getpwname()).  This reduced the fork time by about 15msec.

The init socket function simply calls the winsock initialization
function.  Again I opted for lazy evaluation and took the
call out of the fork code and put it at the start of many
of the socket functions.  This may make socket operations
slightly slower but speeds up fork by about 10msec.  Since
most programs dont use sockets this seems to be a big win to
me.  As a nice side affect, this should get rid of problems people
have been having with winsock dialing on demand each time
an cygwin program is started up.  With my changes it should only
do this when actually socket operations are used.

So with these two pretty minor changes my fork() went from
about 75msec to about 50msec, a 33% speed increase.  The difference
is quite noticeable when running shell scripts and lots
of shell commands (i.e. makefiles).

I am still looking into other possible improvements so I'll defer
posting patches until its all finished and I have done more complete
testing.  If anyone has ideas on other possible improvements to
fork please let me know.   Btw, is there a developers mailing
list that this would be more appropriate for?  It seems that
most people on the general cygwin list are probably not very
interested in things like this.

                                      Tim N.

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".