I am not fully understanding how fat-pointers allow LLVM to devirtualize the function calls. If the allocators are polymorphic then a particular piece of code doesn't know which vtable it will get at run time correct?
You aren't modifying the same object When you have a fat pointer, llvm can tell you are modifying the ptr, bot the vtable You can then cache the function call When your vtable is in the object you are mutating, it needs to read each time
Allocgate: Restructuring how allocators work in Zig
21–30 of 52 posts
Re: Allocgate: Restructuring how allocators work in Zig
#22Earlier quoted context omitted.
You aren't modifying the same object When you have a fat pointer, llvm can tell you are modifying the ptr, bot the vtable You can then cache the function call When your vtable is in the object you are mutating, it needs to read each time
Does LLVM do the inline caching itself? Or does it just enable zig to do this optimization?
This is llvm, bit zig
Re: Allocgate: Restructuring how allocators work in Zig
#23I am not fully understanding how fat-pointers allow LLVM to devirtualize the function calls. If the allocators are polymorphic then a particular piece of code doesn't know which vtable it will get at run time correct?
The difference in results has to do with pointer provenance tracking and aliasing. With both approaches, the first call to an interface function will almost definitely be devirtualized. The problem is that that first call will also modify implementation state. If the implementation function is not inlined (which is common), this is tracked as a modification to the memory region containing the implementation state. But with the fieldParentPtr model, that's the same memory region containing the vtable! So this breaks constant propagation on the vtable and any later calls must always be fully virtual, even if the optimizer can see the whole way from vtable creation to virtual call.
Re: Allocgate: Restructuring how allocators work in Zig
#24Earlier quoted context omitted.
It's unlikely. RAII comes with a surprising amount of complexity. In order to have a reasonably complete language that has RAII and value types, you must also have: - constructors - destructors - overloadable copy assignment operators - placement new - move semantics and rvalue references These features come together or not at all. If you lose any of them, the language becomes less complete. I think Rust and C++ are…
> In order to have a reasonably complete language that has RAII and value types, you must also have: - constructors - destructors - overloadable copy assignment operators - placement new - move semantics and rvalue references Rust has RAII and value types, and does not have constructors, overloadable copy assignment operators, placement new, or rvalue references (though we do of course have a very similar notion to r…
Edit: to clarify, the thing that makes a constructor/destructor useful in this case is the property that it begins/ends an object lifetime according to the language. This lifetime reasoning certainly has benefits, like the ability to have const fields in C++ and the ability to do static checking of lifetimes in Rust. However it also comes with significant complexity, because move semantics are needed throughout the language, and begin/end lifetime tags are needed when implementing data structures that use preallocated backing arrays.
Re: Allocgate: Restructuring how allocators work in Zig
#25Earlier quoted context omitted.
> In order to have a reasonably complete language that has RAII and value types, you must also have: - constructors - destructors - overloadable copy assignment operators - placement new - move semantics and rvalue references Rust has RAII and value types, and does not have constructors, overloadable copy assignment operators, placement new, or rvalue references (though we do of course have a very similar notion to r…
I don't mean that these things need to manifest in exactly the same way as they do in C++, but analagous features are needed. You're right that rvalue references are not necessary, but some form of move semantics are. When I say constructors and destructors, I am really referring to having a concept of object lifetimes as part of the language. Zig does not have this, and is much simpler because of it. Edit: to clarif…
I guess that basically, to me at least, if you've stretched the definitions of these features far enough to include what Rust does, you don't really have a meaningful definition any more.
Re: Allocgate: Restructuring how allocators work in Zig
#26why not just an an interface type instead of this giant mess?
"allocgate" is actually a meme name that we purposely gave to this API change. In reality it's not a big deal and that's the superpower that a language v0 has: you can make breaking changes. As for the builtin interface type, why get locked into one particular implementation when you can have all of them by leaving the choice to the programmer.
Re: Allocgate: Restructuring how allocators work in Zig
#27Earlier quoted context omitted.
> There is a lack of standardized RAII wrappers for handle types like there is for pointers. .. what's wrong with #include template using safe_handle_t = std::unique_ptr ; using file_handle = safe_handle_t ; void file_example() { file_handle f{fopen("foo", "r")}; }
Now you have a double indirection. Worse, the handle probably already references dynamically allocated memory. So that’s two dynamic memory allocations per “safe” handle. Which might be perfectly acceptable for non-critical programs, but it is certainly going to tank your memory usage efficiency and thrash your CPU cache if scaled up to millions of handles.
Re: Allocgate: Restructuring how allocators work in Zig
#28Earlier quoted context omitted.
It's unlikely. RAII comes with a surprising amount of complexity. In order to have a reasonably complete language that has RAII and value types, you must also have: - constructors - destructors - overloadable copy assignment operators - placement new - move semantics and rvalue references These features come together or not at all. If you lose any of them, the language becomes less complete. I think Rust and C++ are…
Rust doesn't end up with an overloadable copy assignment operator, it does something else which I would argue is cleverer (although you can't just add it to an existing language) Because Rust knows the lifetime of everything in your program (in Rust the lifetime of things is part of their type) the effect of the assignment operator = is to dispose of whatever was in the variable before, and move the assigned item int…
Re: Allocgate: Restructuring how allocators work in Zig
#29Earlier quoted context omitted.
> There is a lack of standardized RAII wrappers for handle types like there is for pointers. .. what's wrong with #include template using safe_handle_t = std::unique_ptr ; using file_handle = safe_handle_t ; void file_example() { file_handle f{fopen("foo", "r")}; }
Now you have a double indirection. Worse, the handle probably already references dynamically allocated memory. So that’s two dynamic memory allocations per “safe” handle. Which might be perfectly acceptable for non-critical programs, but it is certainly going to tank your memory usage efficiency and thrash your CPU cache if scaled up to millions of handles.
auto f = fopen("foo", "r");
if(f)
fclose(f);
check for yourself: https://gcc.godbolt.org/z/YxaPqYGezRe: Allocgate: Restructuring how allocators work in Zig
#30Earlier quoted context omitted.
I don't mean that these things need to manifest in exactly the same way as they do in C++, but analagous features are needed. You're right that rvalue references are not necessary, but some form of move semantics are. When I say constructors and destructors, I am really referring to having a concept of object lifetimes as part of the language. Zig does not have this, and is much simpler because of it. Edit: to clarif…
Hm, personally I consider "object lifetimes exist" to be completely different than "constructors", which are a hook into a specific point in some sort of object lifetime cycle. Rust doesn't have the hook, so it doesn't have the feature. Note that I didn't put destructors on my list; the Drop trait does exist in Rust and is the same general idea as destructors. I guess that basically, to me at least, if you've stretch…
When you add RAII on top of lifetimes as a language feature, it creates the need for language support for moves and specialized copies. Or a need to say that types cannot be copied. But you need some sort of tag that says "memcpy doesn't cut it anymore", which is what I mean by overridable copy behavior.