News for hackers. Stuff that matters.
Re:What I Learned Making My Own JIT Language by jerf on Wednesday June 27, 2018 @01:30PM (#17410085)
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 years of non-JIT development.

Even LuaJIT, which is to my understand the closest anyone has come to this, still was retrofitting JIT onto an existing language.

I'm not convinced we're going to see much more performance out of JS, for instance, which is about as fast as a dynamic language can go nowadays. But I wonder what the real limits of a "dynamic scripting" language would be from this perspective.

Edit: Per my other comment about indirection being a performance poison pill, here's an example of an idea where a new scripting language might be able to get a lot of performance. Suppose you keep the ability to dynamically create classes and load code and so forth, so that (just as an example, not necessarily a good idea) you can write code that dynamically connects to a database and loads in tables as classes with automatically defined properties, etc. 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. It isn't completely obvious to me how to do this correctly (by implication, if you can "connect to a database" before this pledge()-like call is done, you have to have a pretty complete runtime available just for that), nor is it obvious to me how this would affect the type system, etc. Maybe it's not possible. But it's the type of thing I mean that would be interesting to have examined by someone, who might be able to concretely show why it's not possible, or, whoknows, make it work. And that's just one idea of how to design a dynamic language from the top for performance, basically off the cuff; who knows what a smart person who sat down and thought about this for a couple of weeks before even beginning to code could come up with.

Another one is that it isn't obvious to me that scripting languages must be all based on hash tables that laboriously can be cast back to structs by the JIT; it seems to me that it would also be feasible to go the other way, and allow users to define things as structs, and if you want to offer hash-like access it would be easy to lay down hash-like access to the struct members (iteration, etc.), and either implicitly or explicitly also offer an "overflow" hash table if desired. This would give at least a bit of locality control. Something something arrays too for array control, and suddenly you're starting cook with gas.

Re:What I Learned Making My Own JIT Language by goatlover on Wednesday June 27, 2018 @01:40PM (#17410164)
Nim and Crystal exist, although they're compiled. For a dynamic language made for JIT performance, there is Julia.
Re:What I Learned Making My Own JIT Language by chriselrod on Wednesday June 27, 2018 @03:40PM (#17411140)
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).

Re:What I Learned Making My Own JIT Language by tigershark on Wednesday June 27, 2018 @05:56PM (#17412181)
It seems like an horribly expensive shenanigan to me. F# can do all the work that you described at compile time using type providers with no runtime cost and no need for a call to pledge()... What will happen if someone forgets to call pledge? Or, even worse, if someone calls pledge and then he does some other dynamic operation? Or if he calls pledge at the right time but one thread has been rescheduled and when it is executed again it will continue to issue dynamic instructions?
Re:What I Learned Making My Own JIT Language by monocasa on Wednesday June 27, 2018 @04:09PM (#17411375)
Dart seemed to be designed with the idea of learning from other dynamic JITed language's mistakes.
Re:What I Learned Making My Own JIT Language by mrmr1993 on Wednesday June 27, 2018 @03:32PM (#17411073)
> 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 by naasking on Wednesday June 27, 2018 @02:08PM (#17410398)
Wren is kind of along these lines: http://wren.io/
Re:What I Learned Making My Own JIT Language by pjmlp on Wednesday June 27, 2018 @01:54PM (#17410279)
I guess you mean Julia, or to pick three from the past Common Lisp, Dylan and Strongtalk.
Re:What I Learned Making My Own JIT Language by jerf on Wednesday June 27, 2018 @02:04PM (#17410369)
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 Lisp, well, I suppose I'll leave that up to the reader. Personally, I observe that in a context where dynamic languages are being discussed in a positive manner, Common Lisp is a dynamic language, but when static languages are being discussed in a positive manner, suddenly Common Lisp is a static language. (In fact, that pattern may hold in general; for any given programming individual language characteristic being positively discussed, Common Lisp will have it, no matter how contradictory the whole set may be.) Under the hood, it must resemble one or the other more strongly, but after years of consuming advocacy, heck if I know which.

Can't speak to the other two.

Re:What I Learned Making My Own JIT Language by lispm on Wednesday June 27, 2018 @03:47PM (#17411204)
Common Lisp systems have supported two ways to develop software:

a) fast incremental development of Lisp code. For that one got Lisp interpreters (executing the code on the s-expression level) and quick incremental compilers. Code then tends to be more on the dynamic side with full debug information.

b) delivery of optimized applications for deployment. We got optimizing native-code AOT compilers (using type hints and type inference), block compilers (optimizing larger batches of code), whole-program Lisp-to-C compilers and application delivery tools with tree shakers. Code then tends to be more on the static side with little or not debug/development information.

Between a) and b) there is a continuum of different approaches - often in the same program in different code sections - where compilers may support different code generation strategies.

Thus one can develop using an interpreter or fast incremental compiler, and then deliver with an optimizing compiler.

Re:What I Learned Making My Own JIT Language by pjmlp on Wednesday June 27, 2018 @03:09PM (#17410893)
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 by aidenn0 on Thursday June 28, 2018 @04:10AM (#17414982)
Common Lisp (CL) as a language is highly dynamic. However the typical implementation is incrementally AOT compiled (when you define a function, it is compiled, but you can redefine functions with few limitations).

CL as a language is essentially untyped[1], but does provide some provision for type hints in the standard. Some implementations extend this to the point where they can generate code as efficient as a typed language. Most implementations can also use these to detect some fraction of type errors when compiling.

Some features in lisp were designed with less flexibility to allow for improved performance while others with flexibility at the expense of performance.

I can't imagine how anyone could ever consider CL to be a static language without horribly misunderstanding the term, the language, or both.

1: You can declare types of variables, but the specification clearly intends this to be for optimization purposes: " Type declarations present in the compilation environment must accurately describe the corresponding values at run time;..." the fact that some implementations use these type hints for more does not IMO change the nature of the language.

Re:What I Learned Making My Own JIT Language by lispm on Thursday June 28, 2018 @06:24AM (#17415606)
> consider CL to be a static language without horribly misunderstanding the term

If you look at the file compilation spec in the standard, the compiler is allowed to inline functions and it can assume that a function in the same file can't be redefined later. -> no late binding for the calls in the file. A function declared by the developers to be inlined, makes them also no longer use late-binding for calls.

Specific implementations may also remove the compiler (see Allegro CL, LispWorks and some others), parts of an interpreter and other stuff of a typical Lisp runtime from a delivered applications. Generally function calls then might no longer use late-binding.

The CL standard might mention some dynamic facilities, but actual implementations might remove parts of that from a delivered application, too.

Example for more static facilities were the block compilation mode of Lucid CL, IIRC CMU CL also had a block compilation mode, some compilers do their own inlining (Allegro CL), mocl/clicc (and some others) do 'static' compilation of whole-programs for a large CL subset to C applications, LispWorks has an extensive delivery system which removes various unused Lisp facilities from an application ( http://www.lispworks.com/documentation/lw71/DV/html/delivery... ) and which can drastically reduce dynamic features.

Re:What I Learned Making My Own JIT Language by aidenn0 on Friday June 29, 2018 @01:41AM (#17422637)
While I see your point, I wonder where to draw the line. Lisp has a stronger history of implementations that limit dynamicism.

Common Lisp as a standard is highly dynamic. Common Lisp as actually implemented and used is sometimes much less dynamic.

Does the existence of Cython mean that Python is a static language? Most python programmers will never use it so I would argue not.

Re:What I Learned Making My Own JIT Language by lispm on Friday June 29, 2018 @02:15AM (#17422738)
The standard is not 'dynamic' - it leaves a lot up to the implementations.

The CL standard is describing more 'static' options than usual for Lisp. Lexical binding is more static than Dynamic Binding. Macros are more static than Fexprs. A file compiler is more static than an interpreter.

Common Lisp was already designed with support for higher-performance implementations on stock hardware.

Python is not a 'language' - but for most people it is the combination of a language and its implementation. In this Python one of the main (THE main?) ways to get better performance is to write the code in C.

Not so Common Lisp - it is a language and a bunch of more or less conforming implementations. There the first and most important way to get better performance always has been to use an optimizing to-C or a to-Machine-Code compiler, code hints and compiler switches (SPEED, SAFETY, DEBUG, ...).

For example the developers of a large CAD system written mostly in Lisp (iCAD, PTC, ...) did not compete with Python at that time, but with C++ based CAD systems. Having a dynamic CLOS implementation is then often a burden: how many objects can be created per second? How many generic function calls are possible per second?

For example LispWorks allows the slot-value function to call a generic function slot-value-using-class. This is slow. Thus by default it is shut off.

Clozure CL has I/O functions over streams. By default streams are not CLOS objects / functions. One can get them as CLOS objects, but one has to do that.

There are many other examples where the implementation provides a dynamic version and a more static version.

The typical example in Common Lisp, the language itself: structures and CLOS classes.

Structures are efficiently compiled with inline access, no slot information, no updates on structure changes, efficient access into the slot (-> no multiple inheritance with changing slot otder).

CLOS classes OTOH are the opposite. More dyanamic, especially in an implementation with the Meta-object Protocol supported.

Whenever you decide to use structures in your program, then you choose 'static & fast'. When you choose CLOS classes then you choose: runtime dynamism and generally slower.

The standard itself does not talk about a specific implementation - the implementations in use and the needs of the developer for their application delivery determines then how 'static' the implementation can be or needs to be.

Re:What I Learned Making My Own JIT Language by taeric on Wednesday June 27, 2018 @02:58PM (#17410803)
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 by dfox on Wednesday June 27, 2018 @03:21PM (#17410987)
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 by kodablah on Wednesday June 27, 2018 @02:06PM (#17410379)
> 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

I think "unknown type at compile time" and "JIT-able constructs" are opposites. You'd end up with a tracing JIT that compiles the same set of code in different, specialized ways based on runtime determination of the the input types as though the lang was static. Which is basically what we have today. You can have optionally typed languages, but you still have to write your runtime and compiler to the lowest common denominator, so you probably want to go more strict if you care about your (runtime or compile-time) compiler and runtime performance (as the Dart devs learned IIRC).

I would assume "JIT-ability" is proportional to the immutable input and output knowledge of the code to be compiled.

Re:What I Learned Making My Own JIT Language by naasking on Wednesday June 27, 2018 @02:10PM (#17410408)
> I think "unknown type at compile time" and "JIT-able constructs" are opposites.

Not opposites exactly. Types enforce a phase separation between compile time and runtime, but there's no reason you can't have a compilation phase at runtime too. This leads to staged programming, like MetaOCaml.

Re:What I Learned Making My Own JIT Language by kodablah on Wednesday June 27, 2018 @02:16PM (#17410464)
Well, to phrase it simply, in my mind a dynamic type is not a very JIT-able construct. I suppose it comes down to what people consider "very JIT-able", but I don't consider the same code possibly JIT'ing in separate ways in the same execution as very JIT-able.
Re:What I Learned Making My Own JIT Language by naasking on Wednesday June 27, 2018 @02:25PM (#17410537)
It depends what you mean by "dynamic type". Since we're talking about some hypothetical dynamic language which is JIT-friendly, "dynamic type" could mean a "staged type" of some kind.

It's kind of like how some static languages use code generation, say, based on a database schema, which is then compiled, except this would happen at runtime. It would have to let you programmatically guide this process dynamically, but the result is very close in performance to a statically compiled language.

Re:What I Learned Making My Own JIT Language by kodablah on Wednesday June 27, 2018 @02:28PM (#17410577)
I mean it in the most traditional sense of the term, a type that nothing can be assumed about it until runtime (especially since GGP mentioned Ruby or Python, type hints notwithstanding). The more you can determine before execution, of course the more you can help your JIT/compiler and it becomes less dynamic. Code generation, macros, type providers, etc do not affect the language's type-dynamicness IMO.
Re:What I Learned Making My Own JIT Language by PeCaN on Thursday June 28, 2018 @12:55AM (#17414171)
Dynamic types are very JIT-able. The first JITs ever were for a dynamically-typed language (Smalltalk).
Re:What I Learned Making My Own JIT Language by stcredzero on Wednesday June 27, 2018 @04:55PM (#17411790)
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

Someone did a study of their Python servers, and found that after an initial phase, almost no dynamic facilities were used at all!

it seems to me that it would also be feasible to go the other way, and allow users to define things as structs, and if you want to offer hash-like access it would be easy to lay down hash-like access to the struct members

I like that. Smalltalk kind of did this, but much less. Objects were just arrays of object references, but certain object references were adorned smallint values. People also wrote hash-like accessor implementations for objects. "Shape changes" to objects of a certain class would require a stop the world re-allocation of all such objects, however.

Re:What I Learned Making My Own JIT Language by striking on Wednesday June 27, 2018 @02:03PM (#17410360)
I know at least in LuaJIT you can use FFI to declare structs beforehand and gain a bunch of performance. But that's not a JIT-provided step. That's just something you have to do by hand.
Re:What I Learned Making My Own JIT Language by filereaper on Wednesday June 27, 2018 @01:39PM (#17410155)
The OMR project gave Ruby and Python a try, Ruby was up and running to a decent point. Python turned out much harder.

The Ruby community went in different direction here [2] with RTL MJIT.

[1] https://github.com/eclipse/omr [2] https://medium.com/square-corner-blog/rubys-new-jit-91a5c864...

Re:What I Learned Making My Own JIT Language by liveoneggs on Wednesday June 27, 2018 @02:16PM (#17410471)
perl6 on MoarVM is starting to grow a JIT
Re:What I Learned Making My Own JIT Language by b2gills on Monday July 09, 2018 @11:56PM (#17495318)
MoarVM has had a JIT since 2014. (MoarVM was announced in January 2014, got a type specializer in March of that year and got the beginnings of a JIT in August of that year)

What it just got recently is a pluggable specializer that allows more interesting optimizations to be written in a higher level language (NQP). Since this is closer to Perl 6 than the VM it has more information with which to decide how to optimize code. (Note that it also means you can do the same with any other language that runs on MoarVM, even if it is very different from Perl 6)