This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-04-30
Channels
- # asami (4)
- # babashka (3)
- # beginners (21)
- # biff (22)
- # cljs-dev (6)
- # clojure (11)
- # clojure-europe (3)
- # clojure-norway (35)
- # clojure-spain (4)
- # clojurescript (14)
- # datalevin (2)
- # emacs (12)
- # exercism (2)
- # fulcro (10)
- # honeysql (4)
- # humbleui (3)
- # hyperfiddle (49)
- # instaparse (1)
- # introduce-yourself (1)
- # missionary (1)
- # off-topic (109)
- # pathom (2)
- # practicalli (21)
- # random (2)
- # rdf (2)
- # releases (2)
- # scittle (3)
- # specter (2)
Idea: of "dynamic runtime", "high performance" and "instant startup", you can only pick two. I rated (very subjectively, hand-waving) some runtimes from 0 (bad) to 1 (good). Would you agree? Are there counter-examples? Does this have to be the case? How would you score runtimes you use on the three axes? • Dynamic runtime. How fast can you change the semantics of your running program? Can you keep your state? If you have to restart / recompile, how long does that take? • High performance. When the runtime has had time to warm up, how fast can we get? Is this runtime suitable for number crunching? • Instant startup. How fast is a cold start? Can this work as a script, so that scripts can be composed into a Unix pileline, with an overall not-terrible experience? -- My speculation: I know borkdude has experimented with starting up Sci and JVM Clojure simultaneously, using Sci for instant startup, and moving over to JVM Clojure for JVM optimizations / compilation, but I don't think that effort got further than experimentation. Another challenge is that sci isn't exactly the same as JVM Clojure, so I suspect one could get nasty bugs if the two runtimes "diverge in execution". I also wonder if Common Lisp might score high on all three. But I haven't used CL in anger, so won't blindly guess a score. Common Lisp hacker opinions welcome! I'm also curious if Clojure is better than Common Lisp on any of these axes. Which further begs the question about why some of us prefer Clojure over Common Lisp, and if that merits a fourth axis. Updated table - moving babahska from 0.9 to 1 on "dynamic runtime" (can't edit images on Slack) after discussion in thread:
| thing | dynamic runtime | high performance | instant startup |
|-----------------------------+-----------------+------------------+-----------------|
| Clojure | 1 | 0.8 | 0.2 |
| Go (programming language) | 0.4 | 0.8 | 1 |
| Rust (programming language) | 0.1 | 0.95 | 1 |
| Javascript | 0.8 | 0.5 | 0.8 |
| Babashka | 1 | 0.4 | 0.9 |
> I also wonder if Common Lisp might score high on all three. Perhaps this is more a matter of effort than anything else. If we had infinite resources, perhaps we could "just" invent a JVM that worked like this. Start out with a simple statically compiled interpreter, then move to compile & JIT optimize stuff. But I'm speculation, I've never written anything like the JVM or Sci. Related, perhaps "jvm bytecode" is a better target for this than "Clojure". So that there are different JVM bytecode interpretation strategies with different performance characteristics.
I got to thinking about this while re-watching Rich's https://www.youtube.com/watch?v=2V1FtfBDsLU talk.
Why does babashka score lower for dynamic runtime than Clojure and lower than instant startup than Go?
instant startup: from my memory, it takes less time to start a compiled Rust or Go program than a Babashka program
dynamic runtime: perhaps they should be the same. I feel like I reach for JVM Clojure when I want to use stuff that doesn't work on Babashka. But as I write this, I feel like scoring bb lower than JVM Clojure is unfair. So perhaps they should be both 1. So perhaps I'm thinking more about the ability to do live programming (with tools like Clerk and Portal).
It depends. It’s a matter of a few ms maybe. The Linux static binary is the fastest
Portal already works with bb. Clerk could work with bb, there isn’t something inherent to bb that makes it less dynamic
I discussed Clerk compatibility for bb with @U5H74UNSF but so far there isn’t enough interest in it to warrant the effort
For me, personally, bb and clerk tend to fit into slightly different workflows, bb for scripts that start and stop, and Clerk for things that keep on running.
I ran into this recently, and "solved" it with a small JVM/bb-compatible library that I used with :local/root
. But if I could have run it all on babashka, I might not have used JVM Clojure. It wasn't performance critical.
Though I 100 % respect that making all this work takes a ton of work.
I wonder if "ecosystem leverage" perhaps should be a column. "We can use all the Maven libs from Clojure" is a good argument for JVM Clojure.
Yes. Might also include nbb: it gives access to the whole npm ecosystem with fast startup
Cherry could become a thing like nbb where the performance column also scores high
Though for Cherry you're limited to what Javascript runtimes can do. Mentioning Scittle also makes sense. I feel like some of Scittle / Cherry / nbb's advantage is on better "time from clojure to working javascript" than Clojurescript. No need to compile, or fast compile. Not sure if that "fits" in the table, though.
right. "no build" isn't something that fits my table. Scittle has "no build". Clojurescript does not have "no build". perhaps "instant build" should be a column. Where bb is very good, go is good, rust and clojurescript are less good.
> I know borkdude has experimented with starting up Sci and JVM Clojure simultaneously, using Sci for instant startup, and moving over to JVM Clojure for JVM optimizations / compilation, but I don't think that effort got further than experimentation not sure if that would have any gains at all? the fast SCI startup comes more from the native image than anything else? starting in SCI and handling over to JVM would mean SCI is also in the JVM which would have the big startup time anyways?
Not if the REPL starts interacting perfectly, instantly!
Then i could run clj2
and have a REPL instantly. And it silently "upgrades" to a JVM REPL over time if I keep it running. Or I can clj2 script.clj
, and it just completes before having to start the full JVM thing.
Speaking of which, you could also list Clojure + native image: good performance + instant startup
> Fennel (lua) is a good contender if it’s for scripting @U050SC7SV how would you rate fennel for "dynamic runtime", "high performance", "instant startup" and "instant build" (0-1)?
I suspect JVM clojure might be better than fennel on "high performance", but I don't dare rate Fennel without experience with it.
> Not if the REPL starts interacting perfectly, instantly! @U3X7174KS yeah but then the challenges would come from the very different execution sematics the JVM has vs SCI. (bytecodes vs meta-circular evaluation)
LuaJIT is actually one of the fastest runtimes in general, wouldnt be surprised if it beats the JVM at places
I’d still choose bb, unless I need a tiny distribuable or platform constraints (esp32 or stg)
on a side note, just as a perf testament of LuaJIT, all of my Neovim conf loads and boots from cold in around ~10-12ms 😄
here's some luajit startup time numbers: https://lua-l.lua.narkive.com/C6bYO5Yu/luajit-startup-performance
in fact Lua's one of the core objectives its to be always embeddable in any C99 system. very portable and nice FFI
I watched https://youtu.be/YXrb-DqsBNU?t=1033 yesterday. I love the "let's just improve something that's already good" perspective. (ref Rich, don't break things)
Then lua is beautiful for its minimalism, embedability and how malleable it is but for ease of use, if you’re familiar with clj and need a scripting language with « batteries » included bb wins everyday. That said I’d prefer lua over alternatives in some contexts (I’d prefer lua over elisp in emacs for instance).
How would you judge Lua's performance characteristics compared to Emacs Lisp? (I'm using Emacs, but I'm not always 100 % happy with performance.)
And Emacs startup is both about "how fast is the Emacs Lisp runtime?" and "what is actually loaded?". If you start lots of stuff in Emacs, that takes longer time than starting a minimal Vim config.
yeah its all subjective. id value lua's sematics over perf when comparing with elisp. having proper modules for instance.
also things like ffi and general ease of using a more general lang than a bespoke one feels nicer. that was my top gripe with vimscript too. superglad with all nvim decisions
> Weztern is a good example as well, for its config Interesting. Terminal emulator implemented in rust, with https://wezfurlong.org/wezterm/config/files.html. Exiting way to build a dynamic system (if lua can be changed runtime) without going the Lisp route.
> (if lua can be changed runtime) without going the Lisp route. fennel achieves this with on the fly compilation: https://github.com/Olical/aniseed achives emacs like behaviour
Fennel also provides a lua -> fennel transpiler, it’s super easy to port lua code over to it
wow, aniseed is interesting. Feels like it's reinventing something very close to Clojure, without Emacs and Emacs lisp.
yeah in my 1 year on emacs, i felt like all im trying to do is make it vim (~4 years of vim before), switched to nvim about 4 years back, never looked back 😄 lua would be my primary reason
I went the "let's make Emacs Vim" route 😁 (I'm using #C01GE5PD249, which defaults to behaving very much like vim, with Vim motions, normal mode, insert mode, etc)
elisp just feels off to me, just like vimscript. thankfully no more
Yeah, I've struggled a bit with Emacs Lisp too. Though I've appreciated getting a different (mutable) perspective on Lisp.
clojure spoils you too, you take immutability for granted and then hit the other lisps 😕
borkdude has started making a https://github.com/borkdude/clj2el and jack rusher has started https://github.com/jackrusher/scimacs (via C FFI I think). Though as far as I know, neither is "ready for mass use yet". Though if you're currently on Vim, I guess you don't care too much right now 🙂
@U3X7174KS Thanks for that zig link. I've been wanting to try out zig for a while but I haven't found a good use case for it yet. I think I'm going to try to write a babashka pod in it
yeah but super happy with emacs progress with things treesitter and nativecomp etc. makes the original vim the one thats the least flexible of the 3. like i always suspected
> the vim9 script has definitely put multiple nails in vim's coffin for me which in your case causes you to prefer Neovim to Vim?
that's the final reason, i had moved over much before for things like lua, treesitter, lsp etc, but vim9 burned the bridges to vim. this one articulates my feelings well: https://www.youtube.com/watch?v=p0Q3oDY9A5s
> I've been wanting to try out zig for a while but I haven't found a good use case for it yet. I think I'm going to try to write a babashka pod in it. Interesting! I've been wanting to learn Zig too, but ... there's so much other stuff I want to learn. Electric Clojure and Datomic on my shortlist right now.
golang is pretty much the optimal language for babashka pods: easy to write, small cross platform binaries, large ecosystem. But I think zig is worth checking out
curiously, as we were talking about this, I cloned https://github.com/nodejs/node/tree/0a3f6a9d073cb03383c3d0211c889c9f25f48c71/./ and tried compiling it. Ran make
, got to know I needed to run ./configure
. Ran ./configure
, something happened. Reran make
.
Did it work?
:drum_with_drumsticks:
No idea. More than one hour later, it's still compiling 🙂
I was curious? Perhaps it woulc be easier than I expected? 🤷 If it had succeeded, I guess I could have tried putting C++ on the table. I don't think it would fare too well.
I have recently compiled GraalVM from source, it was pretty doable and fast to my surprise
$ cd nodejs/node
$ make
[...]
make 8673,30s user 360,50s system 99% cpu 2:30:47,74 total
$ ./node
Welcome to Node.js v21.0.0-pre.
Type ".help" for more information.
> 1 + {}
'1[object Object]'
🎉Nope, just because I wanted to try. I haven't tried compiling any of the others from source.
> I also wonder if Common Lisp might score high on all three. But I haven’t used CL in anger, so won’t blindly guess a score. Common Lisp hacker opinions welcome! I’m also curious if Clojure is better than Common Lisp on any of these axes.
I have used Common Lisp in anger, but it’s been a while, and I was only ever a simple applications programmer, I don’t know much about compiler and/or VM design.
I do think that something like CL’s save-lisp-and-die
is pretty hard to beat for instant startup. (If you haven’t used it, this lets you dump the heap--including any runtime state (of course the line between runtime and compile-time state is always fuzzy in a Lisp)--to disk. You can then start a new Lisp process from this file, and startup is essentially instant. My understanding is that the JVM just does not provide sufficiently low-level access to the heap to do something like this.)
I roll my eyes a little when I hear dynamism mentioned as an excuse for Clojure’s slow startup times, because Common Lisp is arguably much more dynamic (you can do things at runtime like change class and type definitions while preserving identity, modify the reader, etc.) and with save-lisp-and-die
you still get instant startup for very large and complicated applications.
> Which further begs the question about why some of us prefer Clojure over Common Lisp, and if that merits a fourth axis.
I would have a hard time going back to object-oriented, mutable-state-ridden Common Lisp after living in the Clojure world of immutable data structures with homoiconic literal syntax with (usually) a common interface across all the key collection types.
That said, I can imagine implementing those things in Common Lisp (and I believe people have). And modern Common Lisp runtimes have some nice advantages as mentioned above. But Clojure is so fundamentally a hosted language, with so much core functionality dependent on the host system and its libraries, a Clojure-on-SBCL (or even a Clojure-on-its-own-runtime) would kind of be a different beast.
@U060QM7AA @U3X7174KS I would recommend looking in to JVM CrAC. It seems as if save-lisp-and-die
will actually be implementable
Similarly AWS Lambda supports a JVM snapshot now (maybe based on the same tech, don't know). Both only work on linux
> Both only work on linux Which for a category of problems is "enough", specially since we all mostly deploy web services to linux or linux-like vms
I don't want to disregard that crack technology though, it's impressive, and so is save-lisp-and-die :)
yep - could hypothetically use it to make the startup of the clojure part faster, but interpreting your script's code with clj
would still be slow since it would be after the restore, but while only on linux is a bit restrictive
in a sense native-image is similar (imaging) but it has more optimizations (for size and performance) but it comes with limitations that it produces a "closed world"
I think I remember seeing that the .NET Framework (not .NET 5+ (yet?)) version of Clojure starts up way faster than the JVM one, FWIW.
If only I knew how to start that on my macOS, does anyone have a tutorial on that?
Has anybody tried https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/? > The Truffle language implementation framework (henceforth “Truffle”) is an open source library for building tools and programming languages implementations as interpreters for self-modifying Abstract Syntax Trees. The Truffle framework allows you to run programming languages efficiently on GraalVM. It simplifies language implementation by automatically deriving high-performance code from interpreters. And what is it's startup time?
yes, I've looked into this. Truffle for Clojure doesn't really make sense since Clojure already runs on the JVM. Truffle Clojure could make sense for native image but only to enable eval, but then you'd need to basically run everything inside of that truffle interpreter which could defeat the purpose of startup time completely. So all in all, the benefits of implementing Clojure in terms of Truffle aren't that great.
I've also looked into using Java on Truffle to execute Clojure + Clojure programs. This works, but it takes 30 seconds to start up which is way worse than normal JVM Clojure
The biggest benefit of Truffle is that you can interact between different languages in the same host. E.g. running TruffleJS allows you to run JS libraries and get results from them in JVM Clojure
Thanks @U04V15CAJ I was hoping you would respond 🙂
A thoughtful take on ChatGPT in music creation. https://www.youtube.com/watch?v=d_7EsKcn8nw
I’ve been coding clojure for a few months but only now I feel to be a real clojure developer. My skills are definitely improved with this cup.
That cup may be awesome