Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

For a language that’s so low level and performance focused, I’m surprised that it has those extra io and allocator arguments to functions. Isn’t that creating code bloat and runtime overhead?


the answer I've seen when it has been brought up before is that (for allocators) there is not a practical impact on performance -- allocating takes way more time than the virtual dispatch does, so it ends up being negligible. for code bloat, I'm not sure what you mean exactly; the allocator interface is implemented via a VTable, and the impact on binary size is pretty minimal. you're also not really creating more than a couple of allocators in an application (typically a general purpose allocator, and maybe an arena allocator that wraps it in specific scenarios).

for IO, which is new and I have not actually used yet, here are some relevant paragraphs:

  The new Io interface is non-generic and uses a vtable for dispatching function calls to a concrete implementation. This has the upside of reducing code bloat, but virtual calls do have a performance penalty at runtime. In release builds the optimizer can de-virtualize function calls but it’s not guaranteed.
  
  ...
  
  A side effect of proposal #23367, which is needed for determining upper bound stack size, is guaranteed de-virtualization when there is only one Io implementation being used (also in debug builds!).
https://kristoff.it/blog/zig-new-async-io/

https://github.com/ziglang/zig/issues/23367


He's talking about passing the pointers to the allocators and Io objects as parameters throughout the program, not how allocator vtables for calling the allocator's virtual functions are implemented. But context pointers are a requirement in any program. Consider that a context pointer (`this`) is passed to every single method call ... it's no more "code bloat" than having to save and restore registers on every call.


Regarding runtime overhead, I'd assume you would still need an io implementation, it is just showing it to you explicitly instead of it being hidden behind the std lib.

For simple projects where you don't want to pass it around in function parameters, you can create a global object with one implementation and use it from everywhere.


You still have to pass arguments to library functions that need to allocate or do I/O ... but the alternative is worse. This is really a bogus issue ... no one is crying over having to pass a `this` pointer to every single call of a method in other languages. Context pointers are a requirement in any sizeable or multi-threaded program, and Zig gives the user full control over what the context object looks like.


Yeah thing is it's usually better to have allocator in particular defined as a parameter so that you can use the testing allocator in your tests to detect memory leaks, double frees, etc. And then you use more optimal allocators for release mode.


Every class method in other languages receives a hidden argument. Odin passes a hidden context argument that contains the allocator. The alternative is global variables--which you can also use in Zig if you're so inclined. The extra arguments aren't something the Zig language imposes, it's a convention.


Given that Zig has functions which can return functions, maybe you could capture the top level io and allocator and return a struct with a bunch of functions that now have the top scope io and allocator visible.

Don’t know. That’s how people usually get rid of repeat arguments (or OOP constructor).


io and allocator objects each only contain 4 pointers or so. They are very fast to wire up and don't create much overhead at all.


I haven't looked to deeply, but I haven't noticed any performance impact. Inlining probably helps too.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: