> You know, I had brief look through some of the SpeedyCGI code yesterday,
 > and I think the MRU process selection might be a bit of a red herring. 
 > I think the real reason Speedy won the memory test is the way it spawns
 > processes.

 Please take a look at that code again.  There's no smoke and mirrors,
 no red-herrings.  Also, I don't look at the benchmarks as "winning" - I
 am not trying to start a mod_perl vs speedy battle here.  Gunther wanted
 to know if there were "real bechmarks", so I reluctantly put them up.

 Here's how SpeedyCGI works (this is from version 2.02 of the code):

    When the frontend starts, it tries to quickly grab a backend from
    the front of the be_wait queue, which is a LIFO.  This is in
    speedy_frontend.c, get_a_backend() function.

    If there aren't any idle be's, it puts itself onto the fe_wait queue.
    Same file, get_a_backend_hard().
    
    If this fe (frontend) is at the front of the fe_wait queue, it
    "takes charge" and starts looking to see if a backend needs to be
    spawned.  This is part of the "frontend_ping()" function.  It will
    only spawn a be if no other backends are being spawned, so only
    one backend gets spawned at a time.

    Every frontend in the queue, drops into a sigsuspend and waits for an
    alarm signal.  The alarm is set for 1-second.  This is also in
    get_a_backend_hard().

    When a backend is ready to handle code, it goes and looks at the fe_wait
    queue and if there are fe's there, it sends a SIGALRM to the one at
    the front, and sets the sent_sig flag for that fe.  This done in
    speedy_group.c, speedy_group_sendsigs().

    When a frontend wakes on an alarm (either due to a timeout, or due to
    a be waking it up), it looks at its sent_sig flag to see if it can now
    grab a be from the queue.  If so it does that.  If not, it runs various
    checks then goes back to sleep.

 In most cases, you should get a be from the lifo right at the beginning
 in the get_a_backend() function.  Unless there aren't enough be's running,
 or somethign is killing them (bad perl code), or you've set the
 MaxBackends option to limit the number of be's.


 > If I understand what's going on in Apache's source, once every second it
 > has a look at the scoreboard and says "less than MinSpareServers are
 > idle, so I'll start more" or "more than MaxSpareServers are idle, so
 > I'll kill one".  It only kills one per second.  It starts by spawning
 > one, but the number spawned goes up exponentially each time it sees
 > there are still not enough idle servers, until it hits 32 per second. 
 > It's easy to see how this could result in spawning too many in response
 > to sudden load, and then taking a long time to clear out the unnecessary
 > ones.
 > 
 > In contrast, Speedy checks on every request to see if there are enough
 > backends running.  If there aren't, it spawns more until there are as
 > many backends as queued requests.
 
 Speedy does not check on every request to see if there are enough
 backends running.  In most cases, the only thing the frontend does is
 grab an idle backend from the lifo.  Only if there are none available
 does it start to worry about how many are running, etc.

 > That means it never overshoots the mark.

 You're correct that speedy does try not to overshoot, but mainly
 because there's no point in overshooting - it just wastes swap space.
 But that's not the heart of the mechanism.  There truly is a LIFO
 involved.  Please read that code again, or run some tests.  Speedy
 could overshoot by far, and the worst that would happen is that you
 would get a lot of idle backends sitting in virtual memory, which the
 kernel would page out, and then at some point they'll time out and die.
 Unless of course the load increases to a point where they're needed,
 in which case they would get used.

 If you have speedy installed, you can manually start backends yourself
 and test.  Just run "speedy_backend script.pl &" to start a backend.
 If you start lots of those on a script that says 'print "$$\n"', then
 run the frontend on the same script, you will still see the same pid
 over and over.  This is the LIFO in action, reusing the same process
 over and over.

 > Going back to your example up above, if Apache actually controlled the
 > number of processes tightly enough to prevent building up idle servers,
 > it wouldn't really matter much how processes were selected.  If after
 > the 1st and 2nd interpreters finish their run they went to the end of
 > the queue instead of the beginning of it, that simply means they will
 > sit idle until called for instead of some other two processes sitting
 > idle until called for.  If the systems were both efficient enough about
 > spawning to only create as many interpreters as needed, none of them
 > would be sitting idle and memory usage would always be as low as
 > possible.
 > 
 > I don't know if I'm explaining this very well, but the gist of my theory
 > is that at any given time both systems will require an equal number of
 > in use interpreters to do an equal amount of work and the diffirentiator
 > between the two is Apache's relatively poor estimate of how many
 > processes should be available at any given time.  I think this theory
 > matches up nicely with the results of Sam's tests: when MaxClients
 > prevents Apache from spawning too many processes, both systems have
 > similar performance characteristics.
 > 
 > There are some knobs to twiddle in Apache's source if anyone is
 > interested in playing with it.  You can change the frequency of the
 > checks and the maximum number of servers spawned per check.  I don't
 > have much motivation to do this investigation myself, since I've already
 > tuned our MaxClients and process size constraints to prevent problems
 > with our application.

Reply via email to