←back to thread

200 points jorangreef | 1 comments | | HN request time: 0s | source
Show context
logicchains ◴[] No.24293046[source]
I work in HFT, and one of the key concerns when writing low-latency code is "is this code allocating memory, and if so, how can I stop it?" Zig is the perfect language for this use case as none of the standard library implicitly allocates, rather for anything that allocates, the caller must pass in an allocator. The stdlib also provides a handy arena allocator, which is often the best choice.

This is a huge advantage over C++ and Rust, because it makes it much harder for e.g. the intern to write code that repeatedly creates a vector or dynamically allocated string in a loop. Or to use something like std::unordered_map or std::deque that allocates wantonly.

replies(8): >>24293328 #>>24293382 #>>24293469 #>>24293919 #>>24293952 #>>24294403 #>>24294507 #>>24298257 #
voldacar ◴[] No.24293469[source]
Yeah when I heard about this I instantly thought of game engines, but it makes total sense for HFT too. "Modern C++", with all its constant little mallocs and frees is so awful for anything that requires ultra low latency
replies(3): >>24293652 #>>24299417 #>>24300117 #
cycloptic ◴[] No.24293652[source]
Can you explain how this is a problem in modern C++? I was under the impression that all the STL containers (string, vector, list, map, etc.) worked the same and have an allocator parameter. Are there other areas where these are missing? Or is the issue that STL implementations almost always default to an allocator that uses malloc? I'm not trying to dog on Zig here (it's a nice little language) but this just doesn't seem to be something that only Zig can do.
replies(3): >>24294240 #>>24294749 #>>24299554 #
mwkaufma ◴[] No.24299554[source]
Many core modern C++ types don't permit customizing the allocator. E.g. std::function
replies(2): >>24299572 #>>24301268 #
mwkaufma ◴[] No.24299572[source]
Furthermore, C++ dependencies commonly instantiate types like std::vector with the default allocator internally, rather than exposing it to the host application.
replies(1): >>24300100 #
cycloptic ◴[] No.24300100[source]
Thank you for the examples. I'm not sure std::function is a good comparison. After some research it seems this used to be in the spec, but it was removed because nobody supported it correctly and it seems it was too difficult to do it in a type-safe manner anyway: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p030...

The other thing is that Zig doesn't seem to have any real plans to support C++-style closures right now. If they ever find a type-safe way to do it while supporting custom allocators, then that would be interesting, but at the moment I wouldn't say it's any better than C++ in this regard.

I actually have seen some C/C++ libraries that do allow changing the default allocator although it's usually only low-level libraries that bother to do this.

replies(1): >>24359229 #
int_19h ◴[] No.24359229[source]
C++-style closures are unrelated to custom allocators, since they're not heap-allocated.
replies(1): >>24401211 #
mwkaufma ◴[] No.24401211[source]
You are correct that the type the compiler creates for a lambda is allocated in-place, usually on the stack, and perform no heap-allocs. However if you pass it to a std:: function it will be _boxed_ and std::function _will_ heap alloc the space for it. This alloc is what's not customizable.
replies(1): >>24404323 #
int_19h ◴[] No.24404323[source]
That's fair, but std::function is not specifically about lambdas (As Boost.Function, it predates them, in fact) - it's about wrapping an arbitrary callable in a way that allows erasing its type. Idiomatic C++ rarely uses that class - I don't think it's used anywhere else in the standard library, even though it has plenty of higher-order functions etc. Turns out that closures that can only be passed in and not returned are still plenty useful.
replies(1): >>24435168 #
1. mwkaufma ◴[] No.24435168{3}[source]
In my dayjob as a code-reviewer, I see it in code-bases a lot. Between the generic name and elevated status in the std namespace, it's a natural tool for developers to reach for, across experience-levels. I speculate that the boxing side-effects are not well understood, given how many times I have to lift them out of hot-loops (despite the many unverified claims that "oh, LLVM will inline and optimize that away, no worries, teehee").

In general, I have not observed a consensus for 'idiomatic' C++, even within a single project. I say this as someone who wishes there was, because my job would be a lot easier if dependencies were less heterogeneous :)