Of course the obvious question is "What kind of memory management does it use?" It uses a "linear" (actually affine) type system for memory management, with borrowed refs similar to Rust, but evidently without tracking mutability: https://github.com/carp-lang/Carp/blob/master/docs/Memory.md The omission of the kind of mutability tracking that eases multithreading seems like a significant drawback for its target appli…
Carp – A statically typed Lisp, without a GC, for real-time applications
111–120 of 139 posts
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#112Of course the obvious question is "What kind of memory management does it use?" It uses a "linear" (actually affine) type system for memory management, with borrowed refs similar to Rust, but evidently without tracking mutability: https://github.com/carp-lang/Carp/blob/master/docs/Memory.md The omission of the kind of mutability tracking that eases multithreading seems like a significant drawback for its target appli…
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#113Of course the obvious question is "What kind of memory management does it use?" It uses a "linear" (actually affine) type system for memory management, with borrowed refs similar to Rust, but evidently without tracking mutability: https://github.com/carp-lang/Carp/blob/master/docs/Memory.md The omission of the kind of mutability tracking that eases multithreading seems like a significant drawback for its target appli…
If the target is "real-time systems with low enough latency bounds to make GC an issue", the question starts to be "does it have predictable pause times" (Rust, afaik, doesn't due to backing mechanisms behind some of the lower level aspects of memory management and/or use of Rc/Arc, though of course someone could get things better there)
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#114Can somebody please explain or give me some links how FP is supposed to work without a GC? For example Rust has different types of function pointers (Fn, FnMut, FnOnce), to guarantee the possibility of lifetime analysis (so it is arguable to consider it a functional programming language). On the other hand the most common FP languages (OCaml, Haskell) all come with a GC. Or am I wrong in assuming this is a functional…
& Rust is surely a highly imperative language that has absorbed functional idioms (where appropriate), rather than something that falls under the banner of "functional language"?
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#115I was thinking of building my own LISP-like language, but seeing so many pop up on here I don't think I'll bother.
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#116So I immediately went to the manual, and CTRL+F'd for cons. Carp has the following restriction: https://github.com/carp-lang/Carp/blob/master/docs/Manual.md
> If you're calling a dynamic function (something defined with defndynamic, or a built in command) it will be executed right away. This works very much like a classic, dynamically typed Lisp interpreter. The dynamic functions are not available in compiled code! Their main usage is in macros and to programatically control your build settings.
cons, car, and cdr are all "Dynamic" functions that can only run _DURING COMPILE TIME_. Which means the GC is not needed during runtime: all garbage-collection occurs in the compiler.
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#117Earlier quoted context omitted.
If the target is "real-time systems with low enough latency bounds to make GC an issue", the question starts to be "does it have predictable pause times" (Rust, afaik, doesn't due to backing mechanisms behind some of the lower level aspects of memory management and/or use of Rc/Arc, though of course someone could get things better there)
Do you mind posting some reference on the unpredictability of pause times in rust? I'm interested in real-time systems and I'd love to know more.
Namely, when you free an object, whether it involves manually written code to release anything it holds recursively, or through some form of reference counter, you do not know the size of object graph you're freeing. If you add any custom release logic to the mix (destructors, any kind of shutdown logic, etc), you also don't know how much extra work unrelated to simple memory management will be also involved.
This means that naive manual memory management, or reference counting systems, have usually unbounded and unpredictable pause times (in addition to various costs involved in implementation details, whether it's malloc()/free() or refcounting). You do not know if object X going out of scope isn't the sole owner of a huge object graph.
This is one of (though not only) reason manual memory management and refcounting aren't considered good options in real-time compared to static allocation of everything (object pools, static stack analysis & limits) or RT Garbage Collector algorithms, though of course it depends on how deep into the rabbit hole you go.
Garbage Collector systems tend to have more control over pauses, but majority of algorithms used optimise for throughput, not latency or predictability. Thus you have "huge GC pause" as GC uses fastest space/time algorithm and "pays down the debt" for making allocation stupidly fast (often single Compare-And-Swap operation, sometimes not even that if each thread uses separate nursery and can keep local heap-end pointer).
However you can also setup environment towards low latency and predictability, probably the most famous example being IBM Metronome (available in J9 and recently open sourced), which uses a static quanta of time for GC and minor variability is the noise of multiple threads of execution synchronizing. This works by dividing available time between mutator (your "work" code) and collector (GC) statically, so that GC will for example always run for 30ms then mutator for 70ms then GC for 30ms and so on. Similar techniques exist for amortizing pathological performance of reference counting by pushing actual deallocation into separate collector thread or similarly scheduled slice (depends on whether you're fine with possibly unpredictable contention between two threads or want more strict timing).
A lot of very hard real time is more about predictability in the end.
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#118So "cons", and the associated "car" and "cdr" functions are innately the "thing" that needs to be garbage collected in Lisp-like languages. So I immediately went to the manual, and CTRL+F'd for cons. Carp has the following restriction: https://github.com/carp-lang/Carp/blob/master/docs/Manual.md > If you're calling a dynamic function (something defined with defndynamic, or a built in command) it will be executed righ…
If you program in C using the Common Lisp c-mera preprocessor, or any of the other similar systems, it's the same thing.
You're writing everything in S-exps, and the expansions use conses, but the output is C; so that of course that cannot call cons at run time.
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#119Earlier quoted context omitted.
> the perf. is similar to lua, so is not be ideal for low-level/realtime systems Lua runs on a bunch of low level systems, and not just for hobbyists (it's at the core of VxWorks, for example). Can you expand on why you think the language doesn't meet your performance criteria?
I think this is LuaJIT, or? Iiuc Janet is comparable to non-JITed Lua. But I actually have no clue.
However, Lua itself can run on anything with the C stdlib and a C99 compiler. Which means for realtime systems you're not generally going to be talking about LuaJIT.
The speed of Lua will depend on the GC in use. Whilst that is an incremental one be default, Lua is designed to allow you to disable it altogether, use the older reference counting one, replace it entirely with your own allocation system, and so on, so that it can meet hard realtime requirements.
Re: Carp – A statically typed Lisp, without a GC, for real-time applications
#120Earlier quoted context omitted.
Do you mind posting some reference on the unpredictability of pause times in rust? I'm interested in real-time systems and I'd love to know more.
This is more specifically about any reference counted system, though it also includes unique owner approaches and others. Namely, when you free an object, whether it involves manually written code to release anything it holds recursively, or through some form of reference counter, you do not know the size of object graph you're freeing . If you add any custom release logic to the mix (destructors, any kind of shutdow…
Deferred decrement enqueues objects whose reference counts drop to zero onto a "destruction queue" instead of deallocating them immediately. Every time you allocate an object, you work through some fixed amount of the destruction queue, such as two objects, unless it's empty; this amounts to destroying each of the references in the destroyed objects, decrementing whatever objects they refer to. This may drop those objects' reference counts to zero, in which case you add them to the destruction queue. This is very simple, it strictly limits the amount of work needed per allocation or deallocation, thus completely avoiding any large pauses, and it's guaranteed to empty out the queue eventually.
However, it's still reference counting, so it's still inefficient, and it still uses an unpredictable amount of memory, which means that any allocation can fail. Many real-time systems are not amenable to recovering from failures.