Hi Tim, Edsko,
I think Edsko's solution would be a great improvement over the current
situation. As it happens, Facundo and I discovered Edsko's original
hs-plugins patch last week and had a close look at it. We observed the
following:
- Calling eval_ is very slow. On our system, eval_ on a simple
identifier, which is the common case, takes a bit more than 100ms to
complete on average, according to criterion. That's three to four
orders of magnitude slower than the roundtrip latencies of the fast
networks (Infiniband) we use for some applications.
- One particularly confusing aspect of using eval_ for transmitting
static values is that the lexical scope of evaluation is not the scope
at the call site (i.e. at unstatic time on the remote node), nor is it
the scope of the sender (i.e. the module in which 'send' is called on
the local node). The scope is instead that of a bespoke wrapper module
with manually specified imports. The list of imports is not checked
statically, and missing one import by mistake could crash the remote
process. Moreover, I don't see how it's possible to automatically build
a list of imports to approximate either of those scopes using TH.
Because to our knowledge TH can't reify the current list of imported
modules.
- The patch so far doesn't deal with type checking of the eval'ed
string. However, if the sender sticks to using some cunning TH
wrappers to be defined in the library, it might be possible to arrange
for the string to always represent a well typed expression by
construction.
All of the problems above can be solved at the cost of the following
restriction: that the argument to 'static' can't be an arbitrary
expression, only an identifier. Then, instead of using 'eval_', you
can use 'loadPackageFunction' from hs-plugins. That function is much faster
than 'eval_' (approx 500x on my laptop). It is a thin wrapper around
the rts linker.
But observe that using loadPackageFunction for resolving function
identifiers to values means mapping an identifier to linker symbol and
asking some linker to find it. That amounts to what Facundo was
proposing to begin with, modulo the following two details:
- using the system linker rather than the rts linker (we were
under the mistaken impression that the rts linker would be retired soon),
- using a small compiler extension to generate the linker symbols on
the sender side (but no extension for the receiver side).
The raison-d'être of the extension was to make something like
loadPackageFunction work even for private (unexported) functions local to a
module, check that all the right compiler/linker flags are enabled,
and allow the argument of 'static' to be an arbitrary (closed)
expression, not just an identifier.These were listed as problems 1a),
1b) and 2) in a previous email of mine.
My personal opinion is that *all* of
1) the existing RemoteTable based approach using string labels,
2) static eval'ed strings as proposed by Edsko,
3) using loadFunction_ alone
4) using option 3) + small compiler extension.
are useful to have. I would also add
5) a datatype of function tags corresponding to a partial
defunctionalization of the program, as suggested
once by SPJ:
https://groups.google.com/d/msg/parallel-haskell/DQg2ZivEilA/21MQ51MLoUAJ
The pros of each approach:
1) is simple and exists today. Probably also quite fast.
2) is very powerful, requires no compiler support, but is
(relatively!) slow, lacks
static checks, requires the user to get the imports right and fails
for some functions (e.g. private ones).
3) is fast, requires no compiler support so no need to wait for a
future version of GHC to use it, but is slightly less general than 2)
and 4).
4) is equally fast, is even more powerful than 2) because it
transparently supports sending function from any module without
limitation, what's more without the performance cost of 2), but
requires compiler support, which at best won't be released anytime soon.
5) is the simplest of all, requires no linker or compiler support, is
really easy to use when the set of static values is fixed and known in
advance.
I guess a much more important point than the relative merits of this
approach versus that approach is this: we have just catalogued 5
different notions of "references" to static values. There are no doubt
more out there, with their own tradeoffs. distributed-static should be
generic in the underlying notion of "reference", yet continue to
provide a standard interface for resolving references to values and
also support composing references.
To this end, I am preparing a patch that I will submit shortly and
discuss in a separate thread that will refactor a small amount of
distributed-static to make it generic, export StaticLabel and
StaticEval as two instantiations, and hence allow for the user to
define further instantions, in his own modules, if he so wishes.
Tim, it's up to you whether you think any of this stuff should go into
the 0.5 release. My personal feeling is that we ought not to introduce
anything new that might delay that release any further. It can always
be released as 0.6 soon thereafter. At the very least, there is some
non-trivial TH work that would need to accompany Edsko's hs-plugins
patch before it becomes usable. We would be happy to dedicate some
developer time to that, though as I said, for our purposes likely
loadPackageFunction would work better, rather than eval_ (the
necessary TH to use the
former seems much simpler, besides).