[MLton] getrusage and times?

Stephen Weeks MLton@mlton.org
Thu, 14 Jul 2005 16:13:00 -0700

> MLton seems to really like calling:
> getrusage(RUSAGE_SELF, {ru_utime={0, 1999}, ru_stime={0, 3999}, ...}) = 0
> times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 432034594

The call to getrusage comes from the GC keeping track of how much time
it consumes: see {start,stop}Timing in gc.c.  The call to times is to
work around a (now pretty old) Linux kernel bug in getrusage.  The
calls aren't supposed to happen unless someone is (possibly) going to
ask for the information: see needGCTime in gc.c.  Unfortunately there
is a bug, which has been there at least since 20041109, that causes
the information to always be collected.  I've checked in a (one line)

In looking at it now, although the use of weaks is clever, perhaps we
should adopt a more robust, explicit, and portable solution.  What
about adding a function

  val MLton.Rusage.keepGCTime: bool -> unit

or possibly

  val MLton.GC.keepTime: bool -> unit

that would simply set a flag in the gcState, which needGCTime could
check to decide whether or not to call getrusage.  The default value
would be true (to mimic current behavior).

> I was stracing a test server program using my version of MLton state 
> threads and was seeing that all over the place. One thing I noticed, 
> the "nasty_hack" really is nasty, since it seems everytime an _export'd
> method gets called, yet another pair of times() and getrusage() are run.

Calling an exported function does a thread switch to the SML-side C
call handler.  This calls GC_switchToThread, which *may*, as far as I
can tell, do a GC (and hence call getrusage) to make sure there is
enough space for the thread to run.  It's not obvious to me if/why it
would always do a GC.

> The fcntl64s around accept are understandable and seem acceptable since it
> only does it for accept and not recv/send. (MSG_DONTWAIT <-- good
> call)
> fcntl64(4, F_GETFL)                     = 0x2 (flags O_RDWR)
> fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK)  = 0
> accept(4, {sa_family=AF_INET, sin_port=htons(1635), sin_addr=inet_addr("")}, [16]) = 5
> fcntl64(4, F_SETFL, O_RDWR)             = 0

These must be happening because you are calling acceptNB, which if you
look in socket.sml calls withNonBlock, which wraps O_NONBLOCK around
the call.  If you wanted to cut down on those, I imagine you could
lift out the setting to happen once before all the accepts, rather
than each time.  Although you may have to step outside the basis
library to do this.