JIT issues relevant to realtime applications

So Julia will have something other static languages already have for a long time, this is really not an advantage…

I don’t know where you get this impression from. For the numerical part this may be true (and that’s one of the reason why Java Minecraft is so slow). But other parts like network, GUI will be another story.

This is incorrect. I think a large part of Unity is written in C#, not C++ and you definitely can’t just use ir from Julia. Microsoft has specialized compiler for C# and you can directly write and compile shader in C#. This is definitely a killer advantage. And static checking is another one. Besides, C# also have some features for high performance code while Julia doesn’t have. For example, they support stackalloc keyword to force stack allocation.

Still, it’s possible to code a game engine in Julia and many 3A companies use their private game engine instead of the open source one (Unity or Unreal). Rust also has a lot of game engine, 3D, 2D or even textual one. But I don’t think Julia will become a good choice for game developing. It lacks too many infrastrutures and it offers no special benefit than other languages.

I think some MIT researchers choose Julia, the mainstream adopters are still C++.

My point with “people prefer Julia for hard-real-time” is that it’s possible. Mindshare may be missing. Games, which as (soft) real-time, are also possible, I didn’t say easier than with C#, I meant basically no huge road-block.

You can precompile Julia code already, and not use runtime code generation. Getting rid of the LLVM as a dependency is only about distributing smaller binaries. Julia as is isn’t that large compared to huge games…

Interesting, while possibly not always. Julia stack allocates by default, since Julia 1.5, for isbits types. It’s likely stackalloc in C# is implemented by alloca (non-portable in C), _alloca, or _malloca, then possibly not stack-allocating. Dos and Don'ts of stackalloc

For _malloca | Microsoft Learn

If size is greater than _ALLOCA_S_THRESHOLD , then _malloca attempts to allocate on the heap, and returns a null pointer if the space can’t be allocated.

For C: C Language Tutorial => alloca: allocate memory on stack

Do not use alloca() in new code

In Linux: “The alloca () function is machine- and compiler-dependent.”, additionally in FreeBSD: “its use is discouraged.” Rust has at least two libraries: stackalloc — Rust library // Lib.rs

Games make their own “GUI”. I’m not aware of missing support do get graphics to the screen (at least for something comparable to Minecraft), and even if something is missing you can reuse C++ code to do that. I agree access to major game engines would be a plus, but you can do 3D only with Julia.

It provides access to windowing, graphics, audio and network.

I’m not saying writing shaders is better in Julia (than e.g. C#). I doubt it, but it’s possible (or use C++ with):

I’ll respond as to the interest in my part in Julia for game scripting. I’ve been in the game industry my entire career spanning a couple of decades, mostly triple A titles, including some very big games everyone knows.
The core engine I’m using is written in C++ so Julia is being looked at as a scripting language solution for scripting designers primarily. Scripting designers need to know some coding but not as much or in depth as a regular programmer; similar to what’s expected from a Unity developer as far as C# is concerned.

For this audience, there’s a balance to reach between the scripting language’s ability to express their designs, and simplicity; really the language’s ability to protect designers from themselves. Something dynamic where a designer doesn’t have to restart the game after making a change to their scripts is ideal. This alone excludes a lot of scripting languages.

It would also be great if the language was not statically typed and if memory management was automatic (which requires GC). This also excludes a lot of languages. The GC should be as fast and lightweight as possible. The full-on generational GC that copies tons of data and creates unpredictable stalls, such as C#, Java or Python would be undesirable. As an aside, I spent a couple of years charging major publishers an arm and a leg to fix their GC issues in Unity games.

There are other options for game scripting such as Lua, but the performance tradeoff can be immense. In my testing environment Lua has 33-200x worse performance than C++. Ideally, designers need to be able to write code and not have to call someone like me to come and turn it into C++ at some point for performance reasons.

Hashlink is decent, a bit less beginner friendly than other options, a bit less performant than others. Angelscript/Squirrel are a bit outdated and not really supported anymore.

All in all, if the PackageCompiler solves the loading issue, I’d rather use Julia because it’s super fast, dynamic, decent GC, and semi-loosely typed – unless I’m missing something here.

14 Likes

I think Julia is basically what you want, but we have some work to do to make the GC and JIT behavior more latency-friendly, as well as changes to the compiler to make AOT compilation easier and faster:

GC improvements:

https://github.com/JuliaLang/julia/pull/41760 should reduce GC latency on multithreaded systems by making all threads cooperate during marking (the most expensive GC step).

Work on escape analysis in the compiler should eventually allow object finalizers to be run immediately at the end of a scope containing a non-escaping object, which should reduce issues with memory consumption for heap-allocated objects with attached finalizers.

https://github.com/JuliaLang/julia/pull/41616 will emit more GC safepoints, reducing stop-the-world latency for multithreaded programs, although further work is needed to improve this situation.

More GC generations could be added to reduce how many non-incremental GC passes need to be performed (as I understand it).

JIT improvements:

Multithreaded compilation should make it possible to minimize JIT latency on UI threads. I suspect this will be a very difficult PR, but will be endlessly beneficial.

Compiler improvements:

https://github.com/JuliaLang/julia/pull/41936 will allow the compiler and LLVM to be removed from PackageCompiler binaries which don’t require the JIT at runtime.

https://github.com/JuliaLang/julia/pull/40414 will make sysimage generation drastically faster, making it easier to iterate on PackageCompiler-generated binaries.

18 Likes

I would go with a lightweight Scheme/Lisp that has good C (C++) bindings.

Julia is not really a good fit for a scripting language; people use it of course for scripting because they are familiar with it, but if you are yet to make the investment into a new language then perhaps Julia is overkill for this purpose.

There’s a couple of problems with something like Lisp or Scheme. First, designers are overwhelmingly trained in traditional C-style type of languages. Lisp-style languages would seem very alien. Second is performance. If your team of designers need to hire a high pay engineer to turn their code into c++ which becomes a black box that few dare mess with, then the scripting language has failed at its job.
For a few years I did just that. Going from developer to developer charging an arm and a leg from people using languages that didn’t scale and/perform well. A lot of Unity and Unreal projects.
I went into a big project with Sony that was using Lua and they told me, “the game runs at 8 fps. We need it to run at 60”. It mainly involved ripping Lua out.
Speed, dynamic (doesn’t need to be compiled), and the ability to express their designs is difficult to find.
Being lightweight is actually not that important (i mean, up to a point…i wouldn’t dare try JS). C# is one of the most popular game scripting languages because of Unity and I’d say Julia is more lightweight than that.
My second choice is Cython, but to get good performance it has to be converted to C and even though that process can be automated, it would lose its dynamic nature, and debugging it would be terrible.

4 Likes

Possibly, but surface syntax is just one aspect of a language. Julia is much closer to Common Lisp underneath (multiple dispatch), and in practice requires quite a bit of learning not only to reap its performance benefits, but to avoid writing code that taxes the compiler for no good benefit.

2 Likes

I’ve never seen a thread with so much people trying to dissuade someone to use Julia here :joy: :joy:

But since we are there:

Note that Julia has to be restarted if you change constant values (const variables) and if you change the structure of a struct (number and/or types of the fields).

Anyway, you seem to know the field you are willing to test it very well. You can probably try it out, and report your experience, good or bad, we will all learn something with that.

10 Likes

Well if we’re going there, I’m going to chip in with my thought that Julia in a game is certainly better than a lot of other dynamic languages would be. I think it’s absolutely worth trying out!

1 Like

This is actually great dialog, mostly constructive :+1:

So if I change a struct in a module, in an embedded scenario, do I have to restart the whole Julia session or can I just reload the module the struct is in?

2 Likes

In pure julia, yes (even with Revise.jl). This isn’t tied to being embedded or not (in fact, aside from starting the REPL, “pure” julia doesn’t differ from embedded julia at all as far as I know).

Pluto.jl does some shenanigans to make that replacement kind-of work, but I think this comes at the cost of recompiling existing functions (which you kinda have to do, since the struct layout may have changed arbitrarily and thus all old compiled code relying on old field positions in memory etc would break).

3 Likes

Yes. There are some preliminary developing-workflow workarounds, like using named tuples (like this: GitHub - BeastyBlacksmith/ProtoStructs.jl: Easy prototyping of structs). But in general when one is changing very frequently the custom types, one need to do frequent restarts.

5 Likes

Revisiting this topic in 2023, is there any way to stop the JIT from interfering with every single thread ?
I’m testing some interractive audio generation where I have an audio thread running and a main thread for user interraction and as far as I can tell it seems that JIT triggered by the main thread interrupts the audio thread even when it compiles things that don’t directly affect it.

1 Like

Just make sure that JIT does not happen… You only need a (pre-compile) script that calls all functions with all possible type combinations…

I don’t know if that was tongue in cheek given it doesn’t seem trivial to do :slight_smile:
I tried for even a very simple case and it’s proving difficult in practice. For instance I use eval() and want to catch errors and display stack traces, and I wrote a warmup function that purposedly throws before the audio is started.
That works if I write to stdout, but I don’t want to do that so I used an IOBuffer that does nothing, but then the first actual error still introduces a big pause.

eval by definition needs compiler, so you can’t statically compile it.

2 Likes

Of course, I’m not tring to statically compile it, I’m trying to at runtime call it before the audio thread is started, with a few different arguments, to warm up the JIT for stuff where it particularly matters.
My point was that even that doesn’t really work or at least is very hard to do properly.
But coming back to my initial question, I guess it’s not currently possible ?

basically the generally pattern is your have a .jl script that takes some input, you compile it with dummy input, and you get a binary you can run later without Julia compiler.

There’s no room to have a eval() anywhere – you just can’t have eval involved in this process

1 Like

Yes I understand, but that wasn’t quite what I was asking. I guess the answer to the thread interruption is ‘no’.

Responders are being confused here; @JTriggerFish cannot just precompile everything ahead of time, the nature of what they’re doing requires runtime JIT capabilities (for interactive audio synthesis/processing, which sounds very cool!).

Julia’s JIT is not yet “interactive-friendly”, and even once it is, our GC is not. I would consider what was suggested (by you?) in another Discourse post, which is to put code generation into another Julia process. You will then need to figure out how to “ship” that code into your real-time process, potentially by using StaticCompiler.jl to generate a linkable binary? Or you could compile to virtual ISA like WASM and have your real-time process run a WASM VM.

2 Likes