What does 'Zero Cost Abstraction' mean? - rust

I came across this term when exploring Rust.
I saw different kinds of explanations regarding this and still don't quite get the ideas.
In The Embedded Rust Book, it said
Type states are also an excellent example of Zero Cost Abstractions
the ability to move certain behaviors to compile time execution or analysis.
These type states contain no actual data, and are instead used as
markers.
Since they contain no data, they have no actual representation in
memory at runtime:
Does it mean the runtime is faster because there is no memory in runtime?
Appreciate it if anyone can explain it in an easy to understand way.

Zero Cost Abstractions means adding higher-level programming concepts, like generics, collections and so on do not come with a run-time cost, only compile time cost (the code will be slower to compile). Any operation on zero-cost abstractions is as fast as you would write out matching functionality by hand using lower-level programming concepts like for loops, counters, ifs and using raw pointers.
Or another way to view this is that using zero-cost abstraction tools, functions, templates, classes and such come with "zero cost" for the performance of your code.

Zero cost abstractions are ones that bear no runtime costs in execution speed or memory usage.
By contrast, virtual methods are a good example of a costly abstraction: in many OO languages the type of the method's caller is determined at runtime which requires maintaining a lookup table (runtime memory usage) and then actually performing the lookup (runtime overhead per method call, likely at least an extra pointer dereference) with the runtime type to determine which version of the method to call. Another good example would be garbage collection: in return for being able to not worry about the details of memory allocation you pay with GC pauses.
Rust though mostly tries to have zero cost abstractions: ones that let you have your cake and eat it too. Ones that compiler can safely and correctly convert to forms that bear no extra indirection/memory usage. In fact, the only thing that I'm aware of (somebody more knowledgeable correct me if I'm wrong) that you really pay for at runtime in Rust is bounds checking.

The concept of zero cost abstractions originally came from the functional world. However, the terminology comes from C++. According to Bjarne Stroustrup,
In general, C++ implementations obey the
zero-overhead principle: What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better.
This quote along with most answers fail to deliver the idea in its entirety because the context in which these were said isn't explicitly stated.
If there was only one programming language in the world: be it Rust or C++, zero-cost abstractions would be indistinguishable from most other compiler optimizations. The implication here is that there are countless other languages that let you do the same things as Rust or C++, but with a nonzero and often runtime-specific cost.

Related

Does Rust formally distinguish constructs that trade compile-time checks for runtime checks?

There are a number of tricks I've learned about for "getting around" Rust's restrictions without using unsafe. For example:
Option::unwrap
RefCell
There are probably others I'm forgetting.
In cases like these, responsibility for specific aspects of correctness is shifted from the compiler to the programmer. Things that would have been compilation errors become panics, and the programmer is expected to just "know that their logic is right".
Panics are better than memory corruption, but given Rust's branding as a fully-safe language, I would think these "trap-doors" would be formally identified somehow - in the type system, documentation, or otherwise - for easy identification. The programmer should know when they're using a shortcut and taking on added responsibility.
Does this kind of distinction exist? Even just an explicit list somewhere in the documentation? Is my mental model wrong, making such a thing unnecessary?
No, there is no formal distinction.
I believe you are asking if there is an effect system. While this has been talked about by compiler developers for a while, there is no consensus about if it would truly be beneficial or detrimental in the long run.
"getting around" Rust's restrictions
These "get around" nothing. The methods themselves ensure the requirements are upheld.
shifted from the compiler to the programmer
I disagree with this assessment. Responsibility has been shifted from compile time to run time, but the compiler and the library code still ensures that safety is upheld.
using unsafe
Unsafe code truly moves the responsibility to the programmer. However, then that programmer builds safe abstractions that other programmers can make use of. Ideally, they build the abstractions using tools that are checked at compile time, helping to reduce runtime errors.
Rust's branding as a fully-safe language
responsibility for specific aspects of correctness
Yes, Rust intends to be a memory-safe language, which does not mean that code written in Rust is correct. The branding emphasizes memory safety; other people assume that means things like "cannot crash", but we cannot prevent all mistaken interpretations.
See also:
Why does Rust consider it safe to leak memory?

How to reliably influence generated code at near machine level using GHC?

While this may sound as theoretical question, suppose I decide to invest and build a mission-critical application written in Haskell. A year later I find that I absolutely need to improve performance of some very thin bottleneck and this will require optimizing memory access close to raw machine capabilities.
Some assumptions:
It isn't realtime system - occasional latency spikes are tolerable (from interrupts, thread scheduling irregularities, occasional GC etc.)
It isn't a numeric problem - data layout and cache-friendly access patterns are most important (avoiding pointer chasing, reducing conditional jumps etc.)
Code may be tied to specific GHC release (but no forking)
Performance goal requires inplace modification of pre-allocated offheap arrays taking alignment into account (C strings, bit-packed fields etc.)
Data is statically bounded in arrays and allocations are rarely if ever needed
What mechanisms does GHC offer to perfom this kind of optimization? By saying reliably I mean that if source change causes code to no longer perform, it is correctible in source code without rewriting it in assembly.
Is it already possible using GHC-specific extensions and libraries?
Would custom FFI help avoid C calling convention overhead?
Could a special purpose compiler plugin do it through a restricted source DSL?
Could source code generator from a "high-level" assembly (LLVM?) be solution?
It sounds like you're looking for unboxed arrays. "unboxed" in haskell-land means "has no runtime heap representation". You can usually learn whether some part of your code is compiled to an unboxed loop (a loop that performs no allocation), say, by looking at the core representation (this is a very haskell-like language, that's the first stage in compilation). So e.g. you might see Int# in the core output which means an integer which has no heap representation (it's gonna be in a register).
When optimizing haskell code we regularly look at core and expect to be able to manipulate or correct for performance regressions by changing the source code (e.g. adding a strictness annotation, or fiddling with a function such that it can be inlined). This isn't always fun, but will be fairly stable especially if you are pinning your compiler version.
Back to unboxed arrays: GHC exposes a lot of low-level primops in GHC.Prim, in particular it sounds like you want mutable unboxed arrays (MutableByteArray). The primitive package exposes these primops behind a slightly safer, friendlier API and is what you should use (and depend on if writing your own library).
There are many other libraries that implement unboxed arrays, such as vector, and which are built on MutableByteArray, but the point is that operations on that structure generate no garbage and likely compile down to pretty predictable machine instructions.
You might also like to check out this technique if you're doing numeric work and want to use a particular instruction or implement some loop directly in assembly.
GHC also has a very powerful FFI, and you can research about how to write portions of your program in C and interop; haskell supports pinned arrays among other structures for this purpose.
If you need more control than those give you then haskell is likely the wrong language. It's impossible to tell from your description if this is the case for your problem (Your requirements seem contradictory: you need to be able to write a carefully cache-tuned algorithm, but arbitrary GC pauses are okay?).
One last note: you can't rely on GHC's native code generator to perform any of the low-level strength reduction optimizations that e.g. GCC performs (GHC's NCG will probably never ever know about bit-twiddling hacks, autovectorization, etc. etc.). Instead you can try the LLVM backend, but whether you see a speedup in your program is by no means guaranteed.

Why do functional programming languages require garbage collection?

According to Wikipedia, the translation from lambda calculus to combinatory logic is trivial. Concatenative programming languages can rely solely on a stack for memory allocation.
What's stopping GHC from translating Haskell into a concatenative programming language, such as combinatory logic, and then simply using stack allocation for everything?
Is it feasible to do this translation and thus eliminate garbage collection for languages such as Haskell and OCaml? Are there downsides to doing this?
Suppose I have a function that generates a linked list of some size. The size is the function parameter.
The question is: where do I have to allocate memory for the list?
I can't allocate it on the function's stack, since it's invalid after the function is out. And I can't allocate it on the caller's stack, since I don't know how many memory I need to allocate before the function call. So I need to allocate it on the heap.
I think there may be RAII with manual heap management usable, But I can't see how to eliminate heap allocation at all.
Edit
I can't fit all my thoughts in the comment, so I write them here.
There is no magic about stack-based allocation languages. You still need to know when your data is relevant and remove them when they're not.
Imagine you have a separate stack, and your function has control to push and pop data in it. First, there is no automatic memory management anymore, i.e. the function terminates but the data is not deallocated automatically. Second, if you function allocates some memory, needed to support e.g. the list calculation, then all that stuff will be shuffled with the list that you want to return. No chance you can free unused memory (other lists, trees or so) since you have just push and pop operations. If you have other operations, then what is the difference with the heap?
What about few stacks, not one?
You need to allocate them somewhere, manage their growth and sometimes get them back. That stacks are separate constructions that you need manage by hands. No automatic memory management.
Stack-based languages are ok, but forget about the huge amount of algorithms, that was invented with the concept "get memory from somewhere" and "put the memory back", like hash maps, red-black trees, linked lists. Of course, we can allocate all of those structs on a stack, but we can't free their parts if they don't need anymore.
What about "trivial" lambda calculus translation to Turing machine?
Of course, it is trivial, if you resources are infinite. The math theory clarifies nothing about time and memory complexity of such translated constructions. It just approves that both of that models are equivalent, and all that we can say with Turing machine we can say with lambda calculus, and vice-versa. No guarantees that it can work with real-life limitations.
A concatenative programming language is every bit as capable of running out of memory as a functional programming language.
The fundamental challenge garbage collection addresses is freeing memory that is not, or is not known to be, used in a stack-like fashion. It is most especially useful when there is no clear place in the source code that can be pinpointed as the end of the object's lifetime.
If you simply translate a functional language into a concatenative one with only stack allocation, then you will end up overflowing the stack.
There have definitely been various efforts over the years to reduce the need for garbage collection. One interesting (but very complicated) attempt is the region inference system used in the ML Kit. Unfortunately, that's a bit much for most programmers, including myself, to understand. I believe others have worked on such systems since; I don't know the current state of the art.
The take-away is that some very heavy compiler machinery, along with careful programmer discipline and perhaps special annotations, can sometimes reduce or eliminate the need for garbage collection; no trivial transformation is going to do the trick.

Is there any research on (or better use of) of RAII in GC languages?

Note: Object Lifetime RAII not using/with block scope RAII
It seems like its possible using an extra gc category, short lived objects(check gc category somewhat frequently), long lived objects(check gc category less frequently), and resource objects(check gc category very frequently). Or possibly with an extra reference counting gc for resource objects.
It seems like the using/with style can have some benefits by promoting a more functional style(forgive me if I'm wrong and this is not the functional style) of I/O discouraging lots of I/O spread out over the place vs the flexibility of object based RAII (because it's easier). But some problems probably require hard to track resource lifetimes.
Are there reasons besides avoiding gc complexity and speed, that this has not been done on mainstream languages?(I understand that some languages use reference counting as part of gc in their main implementations and as such, RAII may work there, but as I believe their spec doesn't specify reference counting for some type of objects/or all objects and that other implementations used by people don't have reference counting, limiting use of object lifetime RAII in those languages.
P.S.:Do they have c++ type RAII in perl?
Many languages make it a lot easier to write a custom inner block processor† than it was traditionally in C++ (this may have been addressed in the current drafts of the latest standard). When you have these, much of the requirement of the use of RAII for exact resource handling becomes much less pressing; you can do something like this:
using (Transaction t = makeTX()) {
// blah
}
instead of:
{
Transaction t = makeTX();
// blah
}
There's not a huge difference really except that when you have multiple nested using constructs it's much clearer what the order of resource release is. (It's also IMO easier to do special handling in the case where an exception is thrown, useful for things like transactions where you'd want to roll back on error, but I don't expect everyone to agree with me there.) Also note that there are many different ways of writing using constructs, some much more heavyweight than others, but we don't really need to explore the differences here.
Given that exact resource handling is dealt with in this different way, there's a lot less demand for the C++ RAII style and it is viable to use Garbage Collection (GC) instead, as that handles complex cases (i.e., anywhere where it is difficult to tie object lifetime to a specific scope) far more easily. To be fair, there are cases when you need exact resource management with a non-trivial lifetime, but those cases are nasty for everyone.
Perl uses Garbage Collection and has cheap subroutine blocks, as do most other scripting languages in one form or another (because the division between code and data is looser in scripting languages than more traditional compiled langs). The only big scripting language that I'm aware of that doesn't use GC is Tcl, and that's because the value system there is guaranteed loop-free for technical semantic reasons and so reference counting is sufficient. Code blocks are still very cheap there though.
If we look at mainstream compiled languages (i.e., not scripting languages) then we really see a divide in about 1990. Languages from before then (including C++) tend to not assume garbage collection (with some exceptions such as Lisp, Smalltalk and the functional programming languages) whereas languages from after that point (notably Java and C#) do assume GC. I guess there was a substantial philosophical shift about that point, probably coupled to some clever implementations that dealt with the most egregious problems in GC before that point. When you have GC, you simply don't think of RAII as a solution; it's very rooted in C++'s model of the world.
† I just made that term up.

Is there a better C? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I want a better C. Let me explain:
I do a lot of programming in C, which is required for applications that have real-time needs such as audio programming, robotics, device drivers, etc.
While I love C, one thing that gets on my nerves after having spent a lot of time with Haskell is the lack of a proper type system. That is, as soon as you want to write a more general-purpose function, say something that manipulates a generic pointer, (like say a generic linked list) you have to cast things to void* or whatever, and you loose all type information. It's an all-or-nothing system, which doesn't let you write generic functions without losing all the advantages of type checking.
C++ doesn't solve this. And I don't want to use C++ anyways. I find OO classes and templates to be a headache.
Haskell and its type classes do solve this. You can have semantically useful types, and use type constraints to write functions that operate on classes of types, that don't depend on void.
But the domain I'm working in, I can't use Haskell, because it's not real-time capable--mostly due to garbage collection. GC is needed because it's very difficult to do functional programming, which is allocation-heavy, without automatic memory management. However, there is nothing specifically in the idea of type classes that goes against C's semantics. I want C, but with Haskell's dependable type system, to help me write well-typed systems. However, I really want C: I want to be in control of memory management, I want to know how the data structures are layed out, I want to use (well-typed) pointer arithmetic, I want mutability.
Is there any language like this? If so, why is it not more popular for low-level programming?
Aside: I know there are some small language experiments in this direction, but I'm interested in things that would be really usable in real-world projects. I'm interesting in growing-to-well-developed languages, but not so much "toy" languages.
I should add, I heard of Cyclone, which is interesting, but I couldn't get it to compile for me (Ubuntu) and I haven't heard of any projects actually using it.. any other suggestions in this vein are welcome.
Thanks!
Since nobody brought it up yet: I think the ATS language is a very good candidate for a better C! Especially since you enjoy Haskell and thus functional programming with strong types. Note that ATS seems to be specifically designed for systems programming and hard real-time applications as most of it can do without garbage collection.
If you check the shootout you will find that performance is basically on par with C. I think this is quite impressive since modern c compilers have years and years and years of optimization work behind them while ATS is basically developed by one guy. -- while other languages providing similar safety features usually introduce overhead ATS ensures things entirely at compile time and thus yields very similar performance characteristics as C.
To quote the website:
What is ATS?
ATS is a statically typed programming language that unifies implementation with formal specification. It is equipped with a highly expressive type system rooted in the framework Applied Type System, which gives the language its name. In particular, both dependent types and linear types are available in ATS. The current implementation of ATS (ATS/Anairiats) is written in ATS itself. It can be as efficient as C/C++ (see The Computer Language Benchmarks Game for concrete evidence) and supports a variety of programming paradigms that include:
Functional programming. The core of ATS is a functional language based on eager (aka. call-by-value) evaluation, which can also accommodate lazy (aka. call-by-need) evaluation. The availability of linear types in ATS often makes functional programs written in it run not only with surprisingly high efficiency (when compared to C) but also with surprisingly small (memory) footprint (when compared to C as well).
Imperative programming. The novel and unique approach to imperative programming in ATS is firmly rooted in the paradigm of programming with theorem-proving. The type system of ATS allows many features considered dangerous in other languages (e.g., explicit pointer arithmetic and explicit memory allocation/deallocation) to be safely supported in ATS, making ATS a viable programming language for low-level systems programming.
Concurrent programming. ATS, equipped with a multicore-safe implementation of garbage collection, can support multithreaded programming through the use of pthreads. The availability of linear types for tracking and safely manipulating resources provides an effective means to constructing reliable programs that can take advantage of multicore architectures.
Modular programming. The module system of ATS is largely infuenced by that of Modula-3, which is both simple and general as well as effective in supporting large scale programming.
In addition, ATS contains a subsystem ATS/LF that supports a form of (interactive) theorem-proving, where proofs are constructed as total functions. With this component, ATS advocates a programmer-centric approach to program verification that combines programming with theorem-proving in a syntactically intertwined manner. Furthermore, this component can serve as a logical framework for encoding deduction systems and their (meta-)properties.
What about Nimrod or Vala languages ?
Rust
Another (real) candidate for a better C is The Rust Programming Language.
Unlike some other suggestions, (Go, Nimrod, D, ...) Rust can directly compete with C and C++ because it has manual memory management and does not require garbage collection (see [1]).
What sets Rust apart is that it has safe manual memory management. (The link is to pc walton's blog, one of Rusts main contributors and generally worth a read ;) Among other things, this means it fixes the billion dollar mistake of nullpointers. Many of the other languages suggested here either require garbage collection (Go) or have garbage collection turned on by default and do not provide facilities for safe manual memory management beyond what C++ provides (Nimrod, D).
While Rust has an imperative heart, it does borrow a lot of nice things from functional languages, for example sum types aka tagged unions. It is also really concerned with being a safe and performance oriented language.
[1] Right now there are two main pointer types owned pointers (like std::unique_ptr in C++ but with better support from the typechecker) and managed pointers. As the name suggests the latter do require task-local garbage collection, but there are thoughts to remove them from the language and only provide them as a library.
EDITED to reflect #ReneSacs comment: Garbage collection is not required in D and Nimrod.
I don't know much about Haskell, but if you want a strong type system, take a look at Ada. It is heavily used in embedded systems for aerospace applications. The SIGADA moto is "In strong typing we trust." It won't be of much use, however, if you have to do Windows/Linux type device drivers.
A few reasons it is not so popular:
verbose syntax -- designed to be read, not written
compilers were historically expensive
the relationship to DOD and design committees, which programmers seem to knock
I think the truth is that most programmers don't like strong type systems.
Nim (former Nimrod) has a powerful type system, with concepts and easy generics. It also features extensive compile time mechanisms with templates and macros. It also has easy C FFI and all the low level features that you expect from a system programming language, so you can write your own kernel, for example.
Currently it compiles to C, so you can use it everywhere GCC runs, for example. If you only want to use Nim as better C, you can do it via the --os:standalone compiler switch, that gives you a bare bones standard library, with no OS ties.
For example, to compile to an AVR micro-controller you can use:
nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim
Nim has a soft real-time GC where you can specify when it runs and the max pause time in microseconds. If you really can't afford the GC, you can disable it completely (--gc:none compiler switch) and use only manual memory management like C, losing most of the standard library, but still retaining the much saner and powerful type system.
Also, tagged pointers are a planned feature, that ensure you don't mix kernel level pointers with user level pointers, for example.
D might offer what you want. It has a very rich type system, but you can still control memory layout if you need to. It has unrestricted pointers like C. It’s garbage collected, but you aren’t forced to use the garbage collector and you can write your own memory management code if you really want.
However, I’m not sure to what extent you can mix the type richness with the low-level approach you want to use.
Let us know if you find something that suits your needs.
I'm not sure what state Cyclone is in, but that provided more safety for standard C. D can be also considered a "better C" to some extent, but its status is not very clear with its split-brain in standard library.
My language of choice as a "better C" is OOC. It's still young, but it's quite interesting. It gives you the OO without C++'s killer complexity. It gives you easy access to C interfaces (you can "cover" C structs and use them normally when calling external libraries / control the memory layout this way). It uses GC by default, but you can turn it off if you really don't want it (but that means you cannot use the standard library collections anymore without leaking).
The other comment mentioned Ada which I forgot about, but that reminded me: there's Oberon, which is supposed to be a safe(-er) language, but that also contains garbage collection mechanisms.
You might also want to look at BitC. It’s a serious language and not a toy, but it isn’t ready yet and probably won’t be ready in time to be of any use to you.
Nonetheless, a specific design goal of BitC is to support low-level development in conjunction with a Haskell-style type system. It was originally designed to support development of the Coyotos microkernel. I think that Coyotos was killed off, but BitC is still apparently being developed.
C++ doesn't solve this. And I don't want to use C++ anyways. I find OO classes and templates to be a headache.
Get over this attitude. Just use C++. You can start with coding C in C++ and keep gradually moving to better style.

Resources