Open rrnewton opened 8 years ago
Probably somewhat related to #1235.
Note that stack
runs scripts like this in interpreted mode, which is inherently slower than running a compiled executable. My guess would be that the most time is spent in loading up ghc
and type-checking the script.
There is a replacement for runghc
that solves this problem by compiling scripts to machine-code and caching the results: http://hackage.haskell.org/package/runghc. I wonder if we want something like this in stack
.
This caching is not strictly better than the interpreted mode however, since compiling the program to machine code can be slower than interpreting, in case the cache is out of date. This can be quite annoying when developing a script. (Since you will modify the file constantly and always invalidate your cache through that.)
That hypothesis is easy to test. runghc
on the script above takes 400ms on my (wimpy) laptop right now, whereas it still takes 2s under stack.
At the same time, sure, for it would be great to transparently compiler scripts sometimes. This would certainly help with throughput, and since 400ms is ok but not great, maybe it could help there too. But you'd figure that for stack the latency would be similar to stack build
.
@rrnewton Are you on the latest stack? 1.1
comes with some performance enhancements, namely, avoiding loading the hackage index when it's unneeded. More performance enhancements in the pipeline :) (replacing binary
with something faster)
Ah, no I hadn't updated to 1.1. I did just now and do see some perf improvement. That hello-world script drops from 2.0s to 1.5s on this machine.
I'd also be happy for support in caching compiled exes for Stack scripts! It would be the only way to compete with the sub-millisecond bash script execution.
any update? this seems like a good idea.
It looks like things may have already improved substantially. These days, on stack 1.4.0, that hello world script is taking only 700ms. But then again, with time stack --resolver=lts-3.8 runghc hello.hs
, it takes only 300ms. So it looks like 400ms are still spent in the "checking" part, before "doing" begins.
@da-x for bettor or worse, even though stack scripts are not full blown projects (stack.yaml, .cabal, etc), they can import other Haskell files. So that means that if we want to cache exes for scripts, we have to reliably find all the source code imported by the script and include it / hash it when we go to do the cache lookup.
huh, still takes 1.1s for me, then 1.7s when adding a few language extensions and (unused) import statements, to print a "hello". either way, it does seem like only half of the delay is from runhaskell (or runghc? not sure about how they differ), but both halves (say 500ms) are pretty slow (for a script).
btw, any other alternatives besides tuning and caching? maybe a daemon, but that adds further complexity.
anyways, i might also try nix for reproducible (haskell) scripts, but idk what it (nix-shell?) caches or its load speed.
-- stack --resolver lts-9.0 script --package turtle --optimize
stack script --optimize
seems to add 400ms delay to compiled executables.
I absolutely love being able to make self contained shell scripts like this:
But the above hello world takes up to 2 seconds to run on my laptop, even after the first run where the installs occur. Could we get a description of what the fundamental work to do in this scenario is? Which parts of it must be slow, and which parts may have room for improvement?
For example, I would hope the fast path would basically say:
lts-3.8
there already?And it sounds like those should be
O(1)
operations, checking only on a few packages.I'm guessing the current algorithm is more conservative, running some kind of
O(N)
sanity check over, e.g. all the transitive dependencies? If it's something like that, should we work on a fast or trusted mode where only the constant time checks are done?