[MLton-user] Raw conversion of 32 bits into Real32.t
Wesley W. Terpstra
wesley at terpstra.ca
Tue Jan 23 03:14:03 PST 2007
Stephen Weeks wrote:
> val convert =
> withOne
> (fn () => Word8Array.tabulate (4, fn _ => 0wx0),
> fn (a, w) => let
> val () = PackWord32Little.update (a, 0, w)
> val r = PackReal32Little.subArr (a, 0)
> in
> r
> end)
>
> Note that a perfectly valid implementation of One is :
>
> structure One: ONE = struct
> datatype 'a t = T of unit -> 'a
> val make = T
> fun use (T f, g) = g (f ())
> end
The method under discussion is one that is so simple, it should be
possible to write it directly.
Although it's true that MLton sees through this additional level of
indirection, I think it's wrong to think that this means the
complexity is gone. The complexity here is that a programmer looking
at the method can no longer trivially say what it does. Furthermore,
there are different costs associated with the first input to withOne.
As there will only be one implementation of 'One' (forgive the
pun :-) the different optimization choices may not be simultaneously
satisfiable. More indirection means more confusion for maintenance
and more pegs must fit the same hole.
For example, if the 'One' is a shared http connection to google, I
would be very happy to have it use a thread-safe guard rather than
reconstruct/reconnect every time. However, when compared to the cost
of allocating an array (or perhaps even lower) the cost of the guard
may be prohibitive. Also, programmers who are not writing their code
for thread-safety (yet) are very unlikely to use ONE. They could
probably be encouraged to define all variables local that are local.
> So, I don't see much difference between the two "convert" functions,
> except that the "withOne" one has taken care to express the fact that
> the array should be local to each call to convert. While Wesley's
> certainly expresses the same fact, it is not explicit, and so could be
> intentional or not.
I think that it is common knowledge to programmers that variables
defined within method scope are race-condition safe. I also think
that this it is generally good programming style to put a variable
declaration in as tight a scope as possible.
> Also, by not being explicit it loses the chance for the programmer
> to optimize things by using different implementations of One.
As I said, I think this is a red herring. Different cost allocation
methods will prefer different ONE implementations. Certainly you
could have FastOne, SlowOne, etc. However, the cost of indirection to
the programmer remains, and is worsened.
> One other point -- MLton should eventually have a primitive for
> directly doing this conversion, eliminating the need to use an array
> entirely.
For something like this, I agree this is the best solution. However,
I've seen this "temporary array outside of function scope" idiom
several times. I really think that One shouldn't be necessary for
this. Stack allocation could be faster than the temporary array
anyway since the stack is always hot.
On Jan 23, 2007, at 2:58 AM, Matthew Fluet wrote:
> MLton will flatten away some tuples so that they are never
> allocated on the heap. I could imagine doing something similar
> with arrays/vectors of statically known size; it would effectively
> turn the array/vector into a tuple, which would be flattened.
>
> Unfortunately, in this particular example, where we are using the
> array to coerce between Word32.word and Real32.real, it wouldn't
> really work, since we're necessarily accessing multiple Word8.word
> elements at a time.
This approach sounds to me far preferable. I am confused by why
accessing word8 elements would preclude the optimization you described?
Unless this is trivial, of course, I'll shut up and let you work on
the amd64 port. :-)
More information about the MLton-user
mailing list