this post was submitted on 21 Dec 2023
244 points (96.2% liked)

Linux

48310 readers
645 users here now

From Wikipedia, the free encyclopedia

Linux is a family of open source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991 by Linus Torvalds. Linux is typically packaged in a Linux distribution (or distro for short).

Distributions include the Linux kernel and supporting system software and libraries, many of which are provided by the GNU Project. Many Linux distributions use the word "Linux" in their name, but the Free Software Foundation uses the name GNU/Linux to emphasize the importance of GNU software, causing some controversy.

Rules

Related Communities

Community icon by Alpár-Etele Méder, licensed under CC BY 3.0

founded 5 years ago
MODERATORS
 

This isn't Linux, but Linux-like. Its a microkernel built from the rust programming language. Its still experimental, but I think it has great potential. It has a GUI desktop, but the compiler isn't quite fully working yet.

Has anyone used this before? What was your experience with it?

Note: If this is inappropriate since this isn't technically Linux, mods please take down.

you are viewing a single comment's thread
view the rest of the comments
[–] Ramin_HAL9001@lemmy.ml 8 points 11 months ago (2 children)

Is it not possible for Rust to optimize out unused functions as with C?

No Rust can do dead code elimination. And I just checked, Rust can do indeed do FFI bindings from other languages when you ask the compiler to produce dynamically linking libraries, but I am guessing it has the same problems as Haskell when it produces .so or .dll files. In Haskell, things like "monad transformers" depend pretty heavily on function inlining in order to achieve good performance.

So I am talking more about how Rust makes use of the type system to make decisions about when to inline functions which is pretty important when it comes to performance. You usually can't inline across module boundaries unless modules are all statically linked. So as I understand it, if you enable dynamic linking in your Rust program, you might see performance suffer a lot as compared to static linking, and this is why most Rust people (as I understand it) just make everything statically linked by default.

[–] nous@programming.dev 3 points 11 months ago

I am not sure that is quite right. I dont think rust support just enabling dynamic linking of its dependencies. It can talk to dynamically linked libraries - which is how FFI works. And you can compile rust crates to be dynamically linked. But when you are going down this route you are talking over the C ABI. This requires some effort on the code author to make their APIs exportable to C types and means you lose all safety when talking over the C ABI.

I also dont think that rust inlines across a crate boundary unless the function is marked as inline or LTO is enabled - inlining across crate boundaries is expensive and so only done when explicitly needed or asked for it. It is more that you lose features like generics and traits and other things that are not supported over the C API.

[–] someacnt_@lemmy.world 2 points 11 months ago (1 children)

Do you need inlining if you just use fixed monad transformers?

[–] Ramin_HAL9001@lemmy.ml 2 points 11 months ago (1 children)

Do you need inlining if you just use fixed monad transformers?

I am not sure what you mean by "fixed" monad transformers, if you mean writing your own newtype where the functor variable is the only type variable, essentially what you are doing is hand-inlining the monad transformer, and so no, if you inline by hand, then the compiler doesn't need to do it.

Haskell inlines all newtype definitions automatically, so if your monad transformer has all of the type variables bound (except for the functor variable, because that is a special case the Haskell compiler is specifically designed to handle) the compiler will usually reduce those to ordinary lambda expressions automatically, and lambda expressions usually optimize to the most efficient machine code.

The only time the compiler cannot reduce a newtype to an efficient lambda is if the non-functor variables, e.g. the state type variable or the exception type variable, are unbound. Those values could become anything at all at its call site, limited only by the constraints set by the type context. So the type context information, a lookup table of type class instances, must be associated with that lambda expression, and in order to do that, the compiler must create a closure around those values. Creating closures allocates values on the heap, and this is much, much slower than efficient lambda expressions, and no faster than allocating a data constructor as with Free Monads.

Alexis King did a presentation on it where she explains all of this extremely well, if you are interested: https://youtu.be/0jI-AlWEwYI

It is a bit long, but at 17:40 or so she starts talking about strategies for how monads and effects can be implemented in the GHC intermediate code, and compares Free Monads and effects to monad transformers. At 21:15 or so she begins to explain how newtype types can be optimized away completely, newtype constructors don't exist at all in the low-level code, they are a "zero-cost abstraction." On the other hand, data constructors (used for Free monads and effects) always allocate something on the heap which is an order of magnitude slower.

Then at around 27:45 she begins to show how newtypes with type variables cannot be inlined across module boundaries for the reason I explained above (type context tables associated with closures), and so monad transformers cannot be optimized across module boundaries.

[–] someacnt_@lemmy.world 2 points 11 months ago

Yep, I mean like newtype MyT m a = MyT (ReaderT MyEnv (StateT MyState m) a). But one can use ReaderT MyEnv (State MyState m) a directly as well.

I found the MTL style (tagless final) a bit problematic anyway, so I wanted to comment about this.