[MLton-user] Re: Mostly MLton performance. (Stephen Weeks)

Stephen Weeks MLton-user@mlton.org
Wed, 29 Mar 2006 09:03:24 -0800


> Why not just use the following?
> 
>      fun isNan r = (Real.!=(r, r))

In MLton, Real.!= is implemented with the following code.

      val op == =
         fn (x, y) =>
         case (class x, class y) of
            (NAN, _) => false
          | (_, NAN) => false
          | (ZERO, ZERO) => true
          | _ => Prim.== (x, y)

      val op != = not o op ==

"Prim.==" is the primitive notion of equality on reals provided by our
code generator.

So, the implementation you suggest would make two calls to Real.class
(MLton doesn't know that Real.class is functional and so doesn't lift
the common subexpression).

Now, it may be that our implementation of == is unnecessarily
paranoid.  If we implement Real.== directly as Prim.== and define
isNan as you suggest, then things almost work.  Unfortunately, MLton's
optimizer thinks that Prim.== satisfies "x == x", and so the
definition of isNan will be simplified away at compile time to always
return false.  (While this is technically incorrect, it works in MLton
because the only use of Prim.== is in Real.==)

Fortunately, a one line patch to MLton fixes that problem.

----------------------------------------------------------------------
Index: mlton/atoms/prim.fun
===================================================================
--- mlton/atoms/prim.fun	(revision 4379)
+++ mlton/atoms/prim.fun	(working copy)
@@ -1507,7 +1507,7 @@
                               | MLton_equal => t
                               | Real_lt _ => f
                               | Real_le _ => t
-                              | Real_equal _ => t
+(*                              | Real_equal _ => t *)
                               | Real_qequal _ => t
                               | Word_andb _ => Var x
                               | Word_equal _ => t
----------------------------------------------------------------------

With that patch in place you can put the following at the beginning of
a program to override the basis definition of Real.{==,!=,isNan}.

structure Real =
   struct
      open Real

      val == = Primitive.Real64.==
      val != = not o ==
      fun isNan r = != (r, r)
   end

In order to do this, the following must be in your MLB file in order
to expose the nonstandard "Primitive" structure to user code.

local
   $(SML_LIB)/basis/libs/primitive.mlb
in
   structure Primitive
end

Now, my microbenchmarking shows that Real.isNan takes about 6 ns.
I.E. it's now almost twice as fast as C isnan and more than four times
faster than our old Real.isNan.  Excellent!

If no one has any objections regarding correctness, I'd be happy to
commit this improvement.