Live data from Hacker News

What I Learned Making My Own JIT Language

mikedrivendevelopment.com

41–50 of 120 posts

Re: What I Learned Making My Own JIT Language

#41
post #40

Earlier quoted context omitted.

JavaScript is too dynamic for such analysis. JavaScript at its core is not a very well designed language, and it's a bloody miracle that a JIT can make it usable. If you're designing a new language, don't make everything (even arrays!) a hashmap.

[1]In clojure the array type is basically a hashmap to enable high perf/low memory immutability. So I would say "Yes[1]" ^^

I would certainly say "no" to having your basic array type be a hashmap as a hashmap is several orders of magnitude slower than an array for all operations on small arrays and for access for all sizes, although there is nothing wrong with also having hashmap and linked list collections if you need O(1) inserts.

However, what I was referring to is much worse than that:

JavaScript arrays are defined as a normal hashmap with string keys with hooks on assignment to parse the key as a uint32 and update the length property if the value is larger than the current one, and on length assignment iterate over all keys and delete those that successfully parse as a uint32 and has a numeric value larger than the one assigned to length.

Re: What I Learned Making My Own JIT Language

#42
post #21
post #11

Earlier quoted context omitted.

I guess you mean Julia, or to pick three from the past Common Lisp, Dylan and Strongtalk.

Is Julia really a Python competitor, I mean, for the same sort of niches? I had the impression that it was more specialized than that. (Bearing in mind of course that once you're Turing Complete and have a C binding you can technically do anything. Not that either of those two is necessary, but they are sufficient.) But as my phrasing implies, it was just an impression, and two people have mentioned it now. Common Li…

Honestly, the managed environment that is a lisp runtime gets to put a lot of the power into the developer. Just look into the dissassemble abilities of most lisps. Combined with many of the type hints you can give them, it really just changes the dynamic of when things happen. Such that, it really doesn't resemble one more than the other. Just depends what you want.

Re: What I Learned Making My Own JIT Language

#43
post #21
post #11

Earlier quoted context omitted.

I guess you mean Julia, or to pick three from the past Common Lisp, Dylan and Strongtalk.

Is Julia really a Python competitor, I mean, for the same sort of niches? I had the impression that it was more specialized than that. (Bearing in mind of course that once you're Turing Complete and have a C binding you can technically do anything. Not that either of those two is necessary, but they are sufficient.) But as my phrasing implies, it was just an impression, and two people have mentioned it now. Common Li…

Because Common Lisp is powerful enough to get it both ways.

When old timers discuss Common Lisp, usually it goes around the development experience of yore that Allegro and LispWorks still offer, but most FOSS variants fail short of.

As for Julia, yes their main niche is scientific computing. Being able to write everything 100% in Julia, instead of switching to C, C++ or Fortran.

The adoption rate seems to be "slow and steady".

Re: What I Learned Making My Own JIT Language

#44
post #40

Earlier quoted context omitted.

JavaScript is too dynamic for such analysis. JavaScript at its core is not a very well designed language, and it's a bloody miracle that a JIT can make it usable. If you're designing a new language, don't make everything (even arrays!) a hashmap.

[1]In clojure the array type is basically a hashmap to enable high perf/low memory immutability. So I would say "Yes[1]" ^^

[deleted]

Re: What I Learned Making My Own JIT Language

#45
post #40

Earlier quoted context omitted.

[1]In clojure the array type is basically a hashmap to enable high perf/low memory immutability. So I would say "Yes[1]" ^^

I would certainly say "no" to having your basic array type be a hashmap as a hashmap is several orders of magnitude slower than an array for all operations on small arrays and for access for all sizes, although there is nothing wrong with also having hashmap and linked list collections if you need O(1) inserts. However, what I was referring to is much worse than that: JavaScript arrays are defined as a normal hashmap…

Wow that’s depressing ...

Re: What I Learned Making My Own JIT Language

#46
post #21
post #11

Earlier quoted context omitted.

I guess you mean Julia, or to pick three from the past Common Lisp, Dylan and Strongtalk.

Is Julia really a Python competitor, I mean, for the same sort of niches? I had the impression that it was more specialized than that. (Bearing in mind of course that once you're Turing Complete and have a C binding you can technically do anything. Not that either of those two is necessary, but they are sufficient.) But as my phrasing implies, it was just an impression, and two people have mentioned it now. Common Li…

Common Lisp at the language level is similar to Python or Ruby as it has strong dynamic typing. One difference is that CL has optional type declarations which (in contrast to Python's take on the same thing) are really specified as declaring types but what the implementation should do with that information is completely implementation-defined with using it to implement C-style weak static typing being completely legitimate and for some time even popular.

Re: What I Learned Making My Own JIT Language

#47

Sure, there are things that can be "poison pills" for performance, even in JITs. For example, another reason why my fib benchmark beats V8 is that V8 has to continually check if the fib function was redefined as a deoptimization check. I don't allow that in Vaiven, so I can produce faster code. Dart was designed to have fewer of these poison pills, for instance. Note to language designers: Reduce your language's pois…

Is there a list of common language poison pills? I'd like to learn more.

Redefining things are generally a no-no, especially top level things. Knowing what things are and where they are makes things much easier to implement. For inline caches it is much better if you don't redefine things, which applies to JITs.

I remember reading that Mike Pall said about pypy that most of the optimizations he did for luajit were possible for python, but just a lot more work.

Using mutation is a really good way to make most scheme implementations run slowly, since mutation is generally avoided, and thus most flow analysis is spent on checking recursion. A while-loop using set! is generally a lot slower than a recursive named let loop.

Flow analysis is simply easier without mutation, even though it might not look easier for the person reading the code.

Re: What I Learned Making My Own JIT Language

#48
post #4

Sure, there are things that can be "poison pills" for performance, even in JITs. For example, another reason why my fib benchmark beats V8 is that V8 has to continually check if the fib function was redefined as a deoptimization check. I don't allow that in Vaiven, so I can produce faster code. Dart was designed to have fewer of these poison pills, for instance. Note to language designers: Reduce your language's pois…

I would be intrigued to see a modern attempt at a dynamic language like Python or Ruby, but with attention paid from day one to ensure that only very JIT-able constructs were used, and with an eye towards the language helping the user stick to those and be aware of when they deviate, rather than designing a language, setting "runs fast" somewhere around the third or fourth priority, then trying to JIT it after 10-15…

> But instead of working the way the languages do now, instead of constantly walking through all the layers of indirection that can be used to implement all this at every call site and for every call, what if you could do something like the "pledge()" call that says "OK, this is it, I'm all set up, the dynamism is all done, you may now assume that all the type analysis you've done is now complete". Now the JIT can drop all of its paranoia code.

This sounds a lot like Object.freeze[0] in JS, at least for prototypes. I can't remember any of the major engines doing this, but it could be an interesting direction to explore.

[0]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Re: What I Learned Making My Own JIT Language

#49

Sure, there are things that can be "poison pills" for performance, even in JITs. For example, another reason why my fib benchmark beats V8 is that V8 has to continually check if the fib function was redefined as a deoptimization check. I don't allow that in Vaiven, so I can produce faster code. Dart was designed to have fewer of these poison pills, for instance. Note to language designers: Reduce your language's pois…

I say screw deoptimization checks, and put the burden in the redefinition processing. When you redefine something, you stop the world at safepoints and update prior baked assumptions that are registered on the old definition. These redefinitions usually happen during development and initialization, not during performance-sensitive runtime. It would add more to the memory footprint, but that information is not accesse…

I believe truffle ruby does this: http://chrisseaton.com/truffleruby/deoptimizing/

Re: What I Learned Making My Own JIT Language

#50
post #4

Earlier quoted context omitted.

I would be intrigued to see a modern attempt at a dynamic language like Python or Ruby, but with attention paid from day one to ensure that only very JIT-able constructs were used, and with an eye towards the language helping the user stick to those and be aware of when they deviate, rather than designing a language, setting "runs fast" somewhere around the third or fourth priority, then trying to JIT it after 10-15…

Nim and Crystal exist, although they're compiled. For a dynamic language made for JIT performance, there is Julia.

I think Julia is a great example. It also uses a "world-age" for function redefinitions, like another commenter suggested, to put the burden on redefinitions rather than than using runtime deoptimizations. Before implementing the world age, they still didn't deoptimize. This meant that if "bar" called "foo", you call "bar" (causing it to compile), and redefine "foo", and then call "bar" again, it would still use the old foo method. That workaround (redefining "bar") was inconvenient, but unlike with deoptimizing, there was a workaround.

One comment on Julia though. It isn't like other JIT languages. It's a lazy statically compiled language with a REPL. It uses LLVM (like Clang) to compile functions the first time they're called for a given set of input types (every function is like a C++ template by default). This makes it easy to get C-level speed, because all it is is a different front end (with extensive type inference infrastructure/default "auto" for all types) to the same back end.

This is in constrast to an interpreted language, where code only gets compiled when hot (but then with the potential of profile-guided compilation, which could sometimes let them run even faster).

Post reply on HN