[plt-dev] unchecked unsafe operations (was Release Announcement for v4.2.3, third call)

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Sun Dec 13 13:46:35 EST 2009

At Fri, 4 Dec 2009 17:49:45 -0700, Doug Williams wrote:
> I have updated several of the more computationally expensive components of
> the science collection [...] to use the new unsafe operations. [...]
> 
> The question is to what extent you guys as the development team still
> consider these as experimental features.

We previously defined "experimental" as

 I think of it as an experiment in that I'm not sure that this is the
 right approach for better performance. But it seems worth a try, and
 if it works well for many purposes, then it'll stick.

It sounds like the unchecked--unsafe operations are working for you, we
have found other uses in parallel algorithms, and they seem to be
working out in general. So, it's going to stick.


As of yesterday, the bytecode compiler can now improve the chances for
unboxed flonums in the JIT. For example, the bytecode compiler
effectively converts

 (let ([y (unsafe-fl+ c d)]
       [x (unsafe-fl+ a b)])
   (unsafe-fl+ x (unsafe-fl+ y (unsafe-fx->fl (+ q v)))))

to

 (let ([q+v (+ q v)])
   (unsafe-fl+ (unsafe-fl+ a b) 
               (unsafe-fl+ (unsafe-fl+ c d)
                           (unsafe-fx->fl q+v))

so that all the JIT sees all the `unsafe-fl' operations together.

The bytecode compiler can push `let'-bound flonum-producing expressions
into other flonum operations only when the corresponding binding is
used just once. Also, the compiler must be able to tell that the move
doesn't reorder the expression relative to an observable effect. For
example, if you insert a `printf' before the body the of `let' above,
then the `let' bindings are not moved down. I might improve the
transformation to consider specific dependencies, instead of giving up
on all observable effects (e.g., `printf' definitely won't affect the
values of `a' through `d' if none of them are mutable). Already,
though, a potential crash from an unsafe operation, doesn't count for
moving other expressions; for example, the right-hand sides of the `y'
and `x' bindings are reordered above, on the grounds that you cannot
reliably detect an crashes that might happen as a side effect.

The transformation to pull non-flonum expressions out of an `unsafe-fl'
argument position is less constrained. Multiple arguments always can be
lifted to fresh `let' bindings without changing the order of the
expressions. And since potential crashes from unsafe operations don't
count, nested unsafe operations can be kept as immediate arguments
without concern for argument order.

Two main weaknesses remain:

 * It's not so easy to tell when the compiler will be able to avoid
   boxing. The `mzc --decompile' tool now adds `#%flonum' annotations
   on decompiled code to help you double-check, but that's not a great
   help when writing programs in the first place.

 * Flonum results that are used more than once (such as a `let' binding
   of an identifier that is used in two places) are not yet handled by
   the JIT. Adding some form of stack-based local storage, instead of
   always boxing in the heap, is the next step. That change will also
   simplify the rules about when intermediate flonums are unboxed.



Posted on the dev mailing list.