I have seen a lot of older (order 2019) posts about Julia not being suitable for real-time implementations on embedded platforms. Wondering about the current state of thinking on this. I realize the embedded means different things to different people and scale is important. For me, I am talking about small SBCs running Ubuntu Linux and having order 4 of RAM and 32 GB of non-volatile storage. We do mostly signal detection and analysis applications.
I recently started working on such platforms (4 GB RAM, 4 ARM cores @ 1.5 GHz) when the new object-code caching feature became available in Julia 1.9. This new feature has made a huge difference in usability on all platforms, but on these small systems it exposes other shortcomings (e.g. limited memory, I/O bandwidth) that hinder interactivity.
A quick and surprisingly good fix for this is to move the main filesystem to a fast device, e.g., a USB 3.0 drive that can sustain >200 MB/s. Thanks to this one change, I can now spend a good part of my day remotely developing on a Raspberry Pi or one of its Rockchip-based clones (Libre, Khadas, etc) running Julia via Visual Studio Code. Yes, even with the Language Server. No, I do not have the patience of a saint.
Also, Julia 1.9 offers a new command line option (
--heap-size-hint) to keep you out of the swap/thrash zone when precompiling a large project or building a system image. I usually set this to take half of the physical memory, leaving the rest for system processes and the kernel’s block cache.
Hope this helps,
Hey @rehmi , this is really interesting (and welcome to the community! )! To not derail this discussion, would you be able to share more about your set-up and experiences in another Discourse post? I know for a fact several folks would be interested in this notion of running Julia on resource constrained machines – myself included. Thanks!
Important: Enable zram if you have less than 8 GB RAM, in particular if you want to build a custom system image. How to Configure ZRAM on Your Ubuntu Computer - Make Tech Easier
The question is, what do you mean with realtime, and what is the frequency of your control loop. If you need hard realtime you cannot allow any memory allocations. A control loop of 20 Hz that allows a jitter of 5ms might also be possible in the presence of memory allocations.
Thanks for the welcome and for all the comments, suggestions and insights. To give a little context, I would classify our real-time processing as soft real time (i.e., signal processing where we grab signals out of a buffer and our processing needs to keep-up in order to not loose samples) as opposed to hard real time (for example, a control loop where delays or imprecise timing can cause failure of or instability in the control loop). We deploy small underwater systems. Our current prototyping platform is the Khadas VIM3 Pro although we are probably going to migrate to a platform that uses one of the NXP iMX8 family of processors.
For soft real-time have a look at KiteControllers.jl/autopilot.jl at main · aenarete/KiteControllers.jl · GitHub …
GC.gc()before your control loop starts
- do something like:
if t_sim < 0.3*dt t_gc_tot += @elapsed GC.gc(false) end
at each larger time step. This means, do a partial garbage collection if you have at least 30% of the time step left.
- measure the execution time of each time step and count how often you violate your constraint.
For my usecase I am happy if I violate the contraint less than 1% of the time, of course less than 0.1% would be even better…
If you have multiple cores, try to pin your real-time loop to a core that is NOT used by the OS for system IO… This helped a lot when we were implementing a flight controller for a drone on a Rasperry Pi 3…
And of course run your real-time code with real time priority (if using Linux)… But not with the highest priority possible, that might starve some OS threads…