7 ms·
I tried zig after reading so many HN articles and I am not impressed. You have to remember to free after each allocation. The language is supposed to be simple,
by crop_rotation 4y ago
I tried zig after reading so many HN articles and I am not impressed. You have to remember to free after each allocation. The language is supposed to be simple, yet there exist try, catch, defer, errdefer. Rust enums make defining errors so simple with arbitrary payload. Using data structures in rust is much nicer. I think unless you have some precise needs about memory allocation, and want to control exactly where when and how memory is allocated, it is hard to argue in favour of Zig. The allocation control is very nice if you need it though.
- renox 4y ago> I tried zig after reading so many HN articles and I am not impressed. You have to remember to free after each allocation. You write like it was a something surprising, but you know Zig is often described as a replacement for C..
- MrBuddyCasino 4y ago> I think unless you have some precise needs about memory allocation, and want to control exactly where when and how memory is allocated, it is hard to argue in favour of Zig. I do have a hard time to think of use cases other than embedded microcontroller work where that matters nowadays. That said, I suspect that while they could have written TigerBeetle in Rust (no_std does not assume the existence of an allocator), it would have been more difficult.
- AnIdiotOnTheNet 4y ago> I do have a hard time to think of use cases other than embedded microcontroller work where that matters nowadays. People keep saying things like this and software keeps getting more bloated and shittier. It is difficult to believe there isn't a correlation.
- mr_00ff00 4y agoI might be misreading this comment, but the answer to bloated software is not writing all of it in rust/C++/zig. The reasons for bloat is not garage collection.
- AnIdiotOnTheNet 4y agoIt is not about the minutiae of language choices no, it's about a pervasive programmer mindset of not wanting to think about resources like memory of clock cycles or bandwidth as things that actually matter.
- titzer 4y agoA lot of it is that APIs, particularly for GC'd languages, often require lots of intermediate garbage and defensive copies. It wasn't until Java 17 that there was a standard way of parsing integers that didn't allocate. I blame libraries more than languages, TBH.
- kaba0 4y agoSure, I am just confirming that mindset, but can you really reason about the performance impact of that allocation? Even if it does end up allocating due to escape analysis not being sufficient, it will do a thread local pointer bump allocation on a hot, in-cache arena basically, and will just be zero-cost cleared once the still achievable objects are moved. My point is, knowing when to think/not think about allocating and its relevant costs is the proper way to program in a high level language. Only care about it when you are at a part that runs in a hot loop or very often, etc. Pretty much as per the second part of the often partially quoted “premature optimization…”.
- wyldfire 4y agoI think as hardware and software have matured, more and more people have access to develop software. They get correct results without having to dictate every little item. I think it's great, personally. I don't know if it's necessarily "bloat" because it's probably a net win that we wouldn't get some of this software otherwise. The really awesome news is that for those of us who still develop software whose requirements mandate this kind of control - better tools like Zig (and Rust) are making things easier but without requiring things like a GC.
- cmrdporcupine 4y agoThe issue is not always garbage collection. The issue is, as others have pointed out, not having to think about it (memory & cycles.) But this also ties very much into another thing about many modern applications: excessive reliance on third-party packages, many of which are written with (or without) constraints and features that aren't always a match for the core application. It's very easy to blow out your memory use and runtime costs this way. And in this respect Rust could be vulnerable, because Cargo.toml makes it way-easy to pull in a huge tree of transitive third party dependencies without even thinking about it.
- spacechild1 4y ago> I do have a hard time to think of use cases other than embedded microcontroller work where that matters nowadays. Another example: realtime audio applications. There are rather strict rules about what you can and cannot do in the audio callback. In particular, you must not use the system memory allocator (because it may block). Instead you have to pre-allocate memory or use real-time safe memory pools. For this reason, the vast majority of (realtime) audio code is still written in C and C++.
- snek_case 4y agoJIT compilers, games, database server. Anything where you need high performance and do a lot of allocations.
- kaba0 4y agoJIT compilers don’t actually need that low-level programming, and compilers in general use memory in a very haphazard way so they are likely better off with a GC. In fact, you probably loose way more by not being able to write as many/great optimizations in a low-level language than what you lose on a slight overhead — e.g. Java’s Graal JIT compiler is nowadays performing better (written in Java itself) than the “original” C2 compiler which is written in C++. But this is mostly a maintainability question.
- titzer 4y agoI second that; there is nothing inherent about a JIT that requires low-level memory tricks, except that last step of making the machine code executable and callable. I'm not sure that Rust is a good fit for optimizing compilers, either. E.g. most compiler IRs are fundamentally graph-based and use unrestricted cycles. TurboFan in V8 uses arena-style allocation, as do most of the JITs in other JSVMs, mostly because managing every single node is overkill. A GC with a big enough young gen will do well for the kinds of allocation patterns of a JIT.
- snek_case 4y agoFirstly, an arena-based allocator is a custom allocator, which is something most programmers never need to touch and probably don't know even know about. It is a "low-level memory trick". Secondly, we were talking about how Zig makes allocation first-class. This is a prime use case: specifying your own custom arena-based allocator.
- cmrdporcupine 4y agoIndeed, having played with doing compiler / parser / transformation type work (for relational query transformation, etc) in Rust, and got stuck in a maze of borrow-checker disasters around the tree of references... what I'd say is: this kind of thing is actually better done in a higher level statically typed garbage collected language. For myself, I'd use a mature functional something like OCaml/StandardML or a Lisp or similar. It's just easier to not have to worry about borrow checking references and allocation issues when writing stuff like that. The case against garbage collection is where latency is key. P95, P99 latency always suffers under any kind of garbage collector, no matter how advanced. You can minimize pauses, you can move them around, you can play with different collector aspects, but in the end... compare to explicit allocation/destruction/lifetime mgmt, it's going to suffer. I don't think compilers generally have these latency concerns. Throughput matters, but not the odd unpredictable pause. There's only so many application domains where that is super important: Certain kinds of high traffic servers, database engines [not so much query parsing & planning, but execution, data structures, buffer mgmt, networking], operating systems, and games. Also embedded systems where a tight memory profile is important. I like Rust (and Zig) but sometimes when people have a good hammer, they go looking for nails where there aren't any... (All that said, I feel like some tree/DAG-heavy problems in garbage collected languages could benefit from static (and runtime) reference analysis tools that look a lot like Rust's borrow checker, purely for code sanitation/tracing. Also, GC tracing and some of the programming patterns GC encourages can cause havoc on L1 caches reducing throughput as well as a side-effect, so there's that)
- cmrdporcupine 4y agoRe: TigerBeetle... caveat with I haven't looked at the TigerBeetle internals&source code, but. Some thoughts... The kind of page buffer allocation done inside databases is kind of a different nature than the kind where you, e.g. override the default allocator in STL collections, or pass a different allocator into your standard collections library in Zig. It's generally such that only specific data structures -- usually BTrees or HashTables meant to store relations/tables/indexes -- are managed this way. And so they're usually built from scratch around an explicit specific page buffer mgmt system. In some systems (like Umbra or LeanStore) this might be somewhat murkier in that it might use pointer swizzling etc behind the scenes, but it's still usually the case that the data structures used for relation storage are tied directly to the DB's own page buffer implementation anyways. Now, other parts of the application stack may benefit from having a custom allocator in the same way as any performance critical system might, but that's of a different nature. (That said, Rust is also not easily suited to the kind of "pointer-swizzling" behind the scenes bait-and-switch with memory that e.g. LeanStore does with C++. I've tried, and, while it's possible, the language in general gets in the way and you're `unsafe` all over the place anyways.) Anyways, all this to say, TLDR given that all serious databases do explicit memory / buffer pool mgmt ... and build data structures specific to them, I don't see any intrinsic advantage to Zig for this purpose really? Other than it's a decently modern systems programming language that mostly stays out of your way. But... I can see major advantages to using Rust: more developers, bigger community, larger ecosystem, safer for memory mgmt elsewhere in the stack, safer for concurrency, etc.
- kristoff_it 4y agoThis is a good comment that I hope can help other readers manage their expectations. Zig is verbose and low-level and, to like it, you have to appreciate simplicity and what it means to have control over tiny details. If you don't like either thing, then Zig will not bring anything particularly interesting to the table for you.
- handwarmers 4y agoYes to the appreciating simplicity bit, but if you go to Rust expecting to not sweat over having to control tiny details you are in for a ride.
- satvikpendem 4y agoTo be honest I just clone and unwrap everywhere when writing the first iteration. If I care more, then I'll refactor to remove unnecessary clones. I really haven't felt the pain of the borrow checker like some people say.
- aldanor 4y agoAfter you've written your X thousands line of code in Rust, you won't clone/unwrap by default, but just immediately write correct idiomatic code that most often compiles on the first try and doesn't trigger any warnings or clippy warnings, without any added effort. As any other skill, it's just a matter of a bit of practice.
- flohofwoe 4y ago> ...and want to control exactly where when and how memory is allocated... Isn't this the whole point and the reason why manual memory management is still a thing - and actually getting more important with the ever widening CPU/memory latency gap? There simply is no silver bullet for memory management, you can either have high performance or automatic memory management, but not both. (vastly simplified of course - but in languages with automatic memory management - no matter if garbage collection or refcounting - you need to spend so much effort to reduce memory management overhead that it is usually less work to do it all manually in the first place)
- connicpu 4y agoThere's the third option, which is what Rust and C++ do by default, where types have destructors that are run when the value goes out of scope, but also have mechanisms to be activated manually at a precise moment if you know what you're doing and have specific needs.
- haberman 4y agoRAII is not a silver bullet. RAII wants to have neat recursive ownership and cleanup of everything, which is undoubtedly a nice model, but sometimes in low-level code you have to choose between nice recursive ownership and performance. For example, Protocol Buffers are trees of objects. Originally they were simple and had recursively-owned, heap-allocated nodes. But it turns out that traversing and deleting a big graph of heap-allocated objects is expensive, and arenas are more efficient. But arenas make you give up recursive ownership.
- connicpu 4y agoWhich is why low level languages like C++ and Rust make RAII the default in their standard library (easy, simple), but it's still completely possible to manage the memory on your own if that's necessary for the task at hand. Having easy defaults with the capacity to do everything by hand if you need to is the most flexible system.
- chenzhekl 4y agoActually I found rust to be easier to learn than zig, as it is more consistent in most cases.
- hhhhmycmd22 4y agoThat's my complaint about Zig as well coming from C. I feel that there are too many reserved words in Zig, and some of them are too long (comptime for instance). Maybe that's just me being too picky about surface level concerns, but that kind of stuff really bothers me for some reason. Other than that, I do like some parts of Zig. The way templates are designed seems superior to C macros and C++ templates, having your build scripts be in the language you are building is something more languages should do, the compiler is impressive in some ways... I suppose in theory all of that should make up for the poorly chosen keywords and whatnot. I disagree with other commenters that think Zig is simple. At least compared to C, which I think is fair given Zigs target audience. C feels simpler despite all the undefined behavior and extensions to the standard and portability issues. Somehow I think it's down to the reserved words that were chosen.
- kaba0 4y ago> The way templates are designed seems superior to C macros Everything is superior to C macros, that’s like the worst thing ever created. Honestly, C is not a good language but if it would have had at least a proper macro system it could at least be remotely usable, e.g. having proper generic data structures.
- titzer 4y agoI think you might actually like Virgil more. It's actually memory safe and has nice enums. It has no way near the adoption of Zig, nor the standard library or batteries included. But it is my fullest expression of what I think a systems programming language should be.
- _0w8t 4y agoI second that. Several ideas of Virgil like unification of an argument list and tuples or handling of objects are very nice.
- Animats 4y ago"(Zig's) collections are not parametrized by an allocator, like in C++ or (future) Rust. Rather, an allocator is passed in explicitly to every method which actually needs to allocate. This is Call Site Dependency Injection, and it is more flexible.' I'd once considered the idea of having "space" as a type. Space is an array of bytes, and it cannot be read or written. Constructors take in "space" and turn it into an object. Constructors have the privilege of casting "space" to something else. Destructors take an object and turn it back into "space". Destructors can cast their object to "space", and this destroys the data. This separates construction, which is a typed thing, from allocation. It can be used with malloc/free type allocation, garbage collection, or fixed allocation. You'd probably encapsulate this with a generic depending on what type of allocation you are using. C++ probably would have been cleaner if it took that route, but C++ added both generics and allocators as afterthoughts.
- rrishi 4y agoSuper interesting idea.
- crop_rotation 4y agoI actually did find the allocation control very interesting and useful. It seemed like something that can be so useful in the context of request/response servers. Use an allocator per request and reset it at the end. However, it alone does not compensate for all the other missing niceties. e.g. Just dealing with strings is not straightforward compared to Rust/C++.
- ok_dad 4y agoGoddamn, this is a great way to consider this problem! I dabble in a lot of languages, Zig included, and I never had a good way to consider the allocation model that fit in my head in a reasonable way. See, I like to think of software as a transformation process that happens to physical objects that I actually visualize, and so thinking of an allocator as a "space" that can be transformed is super useful to me. I also love very-strictly-typed programming. I enjoy making everything a type, so thinking about a "space" type is something I can use in the future.
- spacechild1 4y ago
- _0w8t 4y agoRust without std lib can control all details of allocations and you still get safe sum types, automatic free etc. The real problem with Rust is hostile syntax for unsafe code that one often needs when doing low-level programming.
- lenkite 4y ago"Rust enums make defining errors so simple with arbitrary payload." enum variants are overused in Rust and are highly expensive in memory IMHO. One tends to run into problems like: https://github.com/serde-rs/json/issues/635 https://github.com/serde-rs/json/issues/635 in Rust projects when used at scale. Value is an enum variant: https://docs.rs/serde_json/latest/serde_json/value/enum.Value.html https://docs.rs/serde_json/latest/serde_json/value/enum.Valu... Comment from that issue: "For example, ElasticSearch returns a 8,683KB document, I deser it into Value and the next RAM reading gives me delta of 98,484KB of RAM use. That's more than 10x the original size." Compare that with a similar issue in simdjson (written in C++), where people are complaining about parsing 4GB documents into a tree !!! https://github.com/simdjson/simdjson/issues/128 https://github.com/simdjson/simdjson/issues/128. serde-json has trouble moving a bullock-cart while simdjson is moving a container ship. I don't like some parts of Zig - I think not having RAII in the language is a terrible mistake. But I do love that anything and everything that can be allocated needs to be passed an allocator. I do think they could have introduced an allocation context for RAII cleanups though instead of forcing the programmer to manually coding defer base cleanups - this is a major source of errors even for advanced programmers.
- Ygg2 4y agoI don't think serde-json has a limit on size. But under some usages it will allocate extra. For example see: https://github.com/serde-rs/json/issues/160#issuecomment-841344394 https://github.com/serde-rs/json/issues/160#issuecomment-841... 2.4 GB file can be read and parsed with serde.
- deleted 4y ago[deleted]
- herbstein 4y ago> Comment from that issue: "For example, ElasticSearch returns a 8,683KB document, I deser it into Value and the next RAM reading gives me delta of 98,484KB of RAM use. That's more than 10x the original size." I think this is misleading. `serde_json` is designed to parse JSON documents directly into your user-defined structs. Thus the raw `Value` type isn't optimized for direct manipulation. That said, what do you expect? The `Value` enum has the `Value::Array` variant that contains a `Vec<Value>`, which is 24 bytes in itself. One for the heap pointer, one for the length, and one for the capacity. Because of alignment requirements the enum discriminant must thus also be aligned at the 8-byte boundary. This gives us a 32-byte type. It is possible to reduce the variant to 16 bytes by making the value type `Box<[Value]>` instead. Ignoring the `Value::Object` variant for a second, that would make `Value` a 24-byte type. However, you've now also made the type immutable. It's a tradeoff. 8 bytes for mutable values. Again, do remember that deserializing into `Value`s is _not_ the primary path when working with JSON through `serde_json`.
- skribanto 4y agotry = ? in rust. catch = match in rust. Zig does have its own general match statement, and the reason it needs a catch is because errors in Zig aren't actually ADT's, but it's not that much more complex. And you're really going to argue that defer,errdefer is more complex than RAII?
- linhns 4y agoYep. I hardly fine a situation where Zig is the best tool to solve the problem. Guess it will fall out of favour with HN readers soon.