Live data from Hacker News

What I Learned Making My Own JIT Language

mikedrivendevelopment.com

81–90 of 120 posts

Re: What I Learned Making My Own JIT Language

#81
post #59

Earlier quoted context omitted.

Then you either need to have "redefinition" checks, or an actual, full stop the world, which is also a performance nightmare and also affects unrelated code. The proper solution is to not have unnecessary dynamics. There is no need for the ability to redefine methods on an object. If you have functions as first-class objects, you can just let users store functions and call them at the cost of indirection if they wish…

> There is no need for the ability to redefine methods on an object In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. Since Lua 5.2 (IIRC) certain metamethods are locked & loaded when you assign a metatable (a prototype d…

> In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary.

It is common, but I do not think it is at all necessary. It always looks nasty whenever I see it in dynamic languages, and I never feel a need to do so in static languages. Whenever functionality is swapable, you'd instead have a function pointer that you call from a permanent method.

> The lesson from languages like Forth, K, and Lua is that the most important thing to optimize isn't JITing, but the software VM itself, including the bytecode and dispatch tables.

I'd very much question this. Properly optimized JIT output should be orders of magnitude faster than interpreted bytecode for even the best interpreter, and if enough is JIT'ed, the interpreter is no longer in play. Contradicting data would be interesting, although I do not generally concern myself with dynamic languages anymore unless I have to.

Re: What I Learned Making My Own JIT Language

#82
post #63

Earlier quoted context omitted.

How many "better designed" dynamic languages are there even out there? Julia is one such language and is substantially faster than V8, at least for idiomatic code. The problem is that most dynamic languages (like JS) are never designed with speed and efficiency in mind. JS just happens to be the fastest of that bunch (other than Lua) because browser makers have invested substantial effort into making it fast - far mo…

Usual suspects that many people who hate JS seem to champion are are python, lua, ruby. I love Lua personally, but it has its own traps too. Never could get into Python too much, the indentation syntax is killing me :P

I hate JS with a passion (I worked with it professionally for years, and wrote crypto and compression algorithms, as well as an entire web browser sandbox), but my refuge is in static languages.

I no longer see dynamic languages useful for more than bash-like scripting where correctness is uninteresting. :/

Re: What I Learned Making My Own JIT Language

#83
post #59

Earlier quoted context omitted.

Then you either need to have "redefinition" checks, or an actual, full stop the world, which is also a performance nightmare and also affects unrelated code. The proper solution is to not have unnecessary dynamics. There is no need for the ability to redefine methods on an object. If you have functions as first-class objects, you can just let users store functions and call them at the cost of indirection if they wish…

> There is no need for the ability to redefine methods on an object In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. Since Lua 5.2 (IIRC) certain metamethods are locked & loaded when you assign a metatable (a prototype d…

I'm not sure fork is a good example. Languages that rely on threads or coroutines (like Java or Go) don't even support the traditional fork semantics. This is a global operation that plays havok with language-level invariants.

Re: What I Learned Making My Own JIT Language

#84
post #59

Earlier quoted context omitted.

> There is no need for the ability to redefine methods on an object In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. Since Lua 5.2 (IIRC) certain metamethods are locked & loaded when you assign a metatable (a prototype d…

> In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. It is common, but I do not think it is at all necessary. It always looks nasty whenever I see it in dynamic languages, and I never feel a need to do so in static languag…

Many language implementations divide their JIT into a "baseline JIT" and a "full JIT". A fast interpreter can do the job of the baseline JIT, with faster startup speed and less latency.

Having a good baseline (either a JIT or just a good interpreter) also means that you don't need to rely as much on the full JIT. This is important because it is actually hard to spend 100% of the time in the JIT -- poison pills abound.

Re: What I Learned Making My Own JIT Language

#85

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.

The poison-pills are implementation dependent. At the end of the day it is just a matter of the JIT not optimizing a certain programming pattern. Sometimes it is just because the authors didn't get around to that yet. Sometimes there is a more fundamental reason why optimizing that pattern would be hard.

I am most familiar with LuaJIT. It has wiki page with a list of things that force it to fall back to the interpreter

http://wiki.luajit.org/NYI

Re: What I Learned Making My Own JIT Language

#86
post #59

Earlier quoted context omitted.

> There is no need for the ability to redefine methods on an object In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. Since Lua 5.2 (IIRC) certain metamethods are locked & loaded when you assign a metatable (a prototype d…

> In prototype-based languages like JavaScript and Lua redefining methods on an object is common at runtime, which makes them quite challenging to JIT. It may be most common early in execution, but there's no way to semantically define that boundary. It is common, but I do not think it is at all necessary. It always looks nasty whenever I see it in dynamic languages, and I never feel a need to do so in static languag…

> Properly optimized JIT output should be orders of magnitude faster than interpreted bytecode

Say we had code like,

  a + b
Statically compiled code might look like

  load a from stack to register
  load b from stack to register
  add a and b
A VM would look like

  load opcode from state to register
  compute opcode address # nullop or two loads and index
  jump to opcode block
  load a from state to register
  load b from state to register
  add a and b
All of that is easily pipelined, especially by the very latest processors which speculate through indirect jumps (which is why we have Spectre, etc). The above is idealized but well reflects, I think, how modern register-based software VMs work.

But when you have a JIT for a dynamically typed language, the entry and exit points of both interpreted sequences and JIT'd sequences require many more instructions to manage bookkeeping, exploding the cost. JIT'ing only works if you can compile blocks of code large enough that the benefits exceed the bookkeeping costs. But that's a tall order for dynamically typed languages where runtime mutations can invalidate JIT'd blocks at many points in a sequence, such as with prototype-based languages.

Getting "[p]roperly optimized JIT output" is the crux of the problem. It takes significant instrumentation and indirection to create and maintain "[p]roperly optimized JIT output". You can't compare the optimized machine code sequences to the analogous interpreted sequences, independent of the surrounding machinery.

Much of the performance benefit of statically compiled code isn't in execution, per se, but in the data structures. A language like Lua is constantly indexing hash tables[1] for even simple record objects, whereas in C you're usually doing direct memory references. But transforming hash table lookups in a dynamic language into direct memory references a la statically compiled C structs is extremely hard if not impossible. Engines like V8 manage to do it much of the time in the context of loading prototype methods, but for ad hoc runtime data structures I don't think it can optimize that at all.

But if your code is primarily operating on, e.g., JSON trees, it wouldn't matter one way or another. If your statically compiled code isn't benefiting from direct memory addressing of data (as is the case with many types of applications) then statically compiled, JIT'd, and interpreted code can have similar runtime profiles, and in many cases you can't even be sure which will be faster in real-world systems.

[1] Lua has opcodes for this so the cost is fixed and small relative to raw C code doing the lookup. And strings in Lua are interned so lookup is usually as simple as a mask and direct index into an array.

Re: What I Learned Making My Own JIT Language

#87
post #70

Earlier quoted context omitted.

a JIT is one! time is wasted doing compiling while running, the compiler has strict time constraints so can't do as many optimizations, claims about theoretical benefits from runtime data collection and-recompilation for optimization are rarely reazlied in the actual world and profile guided optimizations can achieve similar things for AOT compiled programs. soooooooooooo AOT master race

As someone who has ported racket's for loops to guile scheme (or at least, re-implemented them without looking at the source of the original racket macros), a JIT has several benefits. There are some things that you just can't know at compile time, and hot paths/inline caches solve a lot of those things. for example (for/list ([a (in-range a b c)]) (+ a 100)). Since in-range has to check when to end the loop, it has…

You can avoid the test by generating two loops (one that goes up and one that goes down). But that can lead to code bloat...

Re: What I Learned Making My Own JIT Language

#88

I wish http://terralang.org got more attention. It’s a great way to write a jitting library. Add in LuaPEG and it’s a great way to write a jitting language.

I'm not sure I'd put Terra together with dynamic language JITs. It a low-level statically typed language that is closer to C than to Lua.

Re: What I Learned Making My Own JIT Language

#89

Title should say 'JIT compiler'.

Is it not fair to say that this is the same thing? My impression was that in designing a compiler you're explicitly determining the rules for language, which is the same as designing the language itself? Or do you mean that technically the compiler, not the language, is JIT? This is a new area for me so apologies if I'm just misunderstanding the mechanics here!

In theory the programming language is independent from the implementation. You could write an alternate implementation if you wanted.

JIT-ness is a concept related to the implementation, not to the language.

Post reply on HN