1,148 karma · joined February 4, 2018
[0] https://github.com/clangd/clangd/issues/1293 [1] https://github.com/snu-sf-class/swpp202401/issues/21
You can also specify a `-DUNITY_BUILD_BATCH_SIZE` to control how many get grouped, so you can still get some parallelism. However, I think it'd be more natural to be able to specify number of batches (e.g. `nproc`) than their size.
Code bases may need some updating to work.
I think it's important to differentiate between malloc implementations/algorithms, just like it's important to differentiate between GCs. E.g., mimalloc "shards" size classes into pages, with separate free lists per page. This way, subsequent allocations are all from the same page. Freeing does not free eagerly; only if the entire page is freed, or if we hit a new allocation and the page is empty, then it can hit a periodic slow path to do deferred work. https://www.microsoft.com/en-us/research/uploads/prod/2019/0...
Good malloc implementations can also employ techniques to avoid fragmentation. It's unfortunate that the defaults are bad.
But I confess, compacting GCs and profiling the effects of heap fragmentation (especially over time in long running programs) are both things I lack experience in. Microbenchmarks are unlikely to capture that accurately.
> Lies people believe... Calling free() gives the memory back to the OS.
I believe calling `free()` gives the memory back to the allocator, which is much better than giving it to the OS; syscalls are slow. Perhaps not immediately; mimalloc only makes frees available to future `malloc`s periodically.
Trying a simple benchmark where I allocate and then immediately `free` 800 bytes, 1 million times, and counting the number of unique pointers I get: glibc's malloc: 1 jemalloc: 1 mimalloc: 4 Julia's garbage collector: 62767
62767, at about 48 MiB, isn't that bad, but it still blows out my computer's L3 cache. Using a GC basically guarantees every new allocation is from RAM, rather than cache. This kills performance of any heavily allocating code; we don't care only about how fast memory management can work, but how quickly we can worth with what it gives us. I gave a benchmark in Julia showcasing this: https://discourse.julialang.org/t/blog-post-rust-vs-julia-in...
Malloc/free gives you a chance at staying hot, if your actual working memory is small enough.
Allocators like mimalloc are also designed (like the compacting GC) to have successive allocations be close together. The 4 unique pointers I got from mimalloc were 896 bytes apart.
My opinions might be less sour if I had more experience with compacting GCs, but I think GCs are just a vastly more complicated solution to the problem of safe memory management than something like Rust's borrow checker. Given that the complexity is foisted on the compiler and runtime developers, that's normally not so bad for users, and an acceptable tradeoff when writing code that isn't performance sensitive. Similarly, RAII with static lifetimes is also a reasonable tradeoff for code not important enough for more bespoke approaches. The articles example is evidently one of those deserving a more bespoke solution.
https://sourceware.org/glibc/wiki/MallocInternals
So glibc's malloc will use up to 8x #CPUs arenas. If you have 10_000 threads, there is likely to be contention.
They're thus going after a very different market.
I don't find ownership models that difficult. It's things one should be thinking of anyway. I think this provides a good example of where stricter checking/an ownership model like Rust has makes it easier than languages that do not have it (in this case, C++): https://blog.dureuill.net/articles/too-dangerous-cpp/
Of course, if the scheduler ran much slower so your CPU spends more time on the scheduler than time saved through better resource allocation, then it wouldn't help performance. But that's hopefully not a major factor here.
Yet I can't find many benchmarks.
Frustratingly, it's listed here, but not actually included in the results: https://github.com/daanx/mimalloc-bench Might be fun trying to run the benchmarks myself.
Not relevant to this example, which is `popcount`ing only a single number, but AVX512 did also introduce SIMD popcount instructions for getting many inputs at a time. Also true for other useful bit-funs like leading or trailing zeros. So if you're using zmm registers, you can do way better than that godbolt example.
Apparently a lot more claiming evidence exists than sharing of actual evidence. One of the few things shared was a pseudoscientific explanation that stated precisely the opposite of your (and my) subjective experience: That using the mouse is less distracting and can be better committed to muscle memory than "command keys".
I think that is patently false, and don't thing Tog should be taken as anything but a marketing hack.
Disclaimer: my knowledge of Tog is limited to what is presented in that Dan Luu article.
But I would feel bad if, e.g., insurance premiums were to go up because I had inherited risk factors for some costly diseases.
const int y = 42;
const int x = y;
and now modifying x suddenly becomes undefined behavior, and x + 1 returns 43
https://godbolt.org/z/cqbGasrMM
Similar to how the original assembly showed making `y` const removed the reload and increment/+1 of x.So, it seems that all these compilers agree that modifying `x` is undefined behavior if `y` is const, but is defined if `y` is mutable.
I am not sure why this is.
I may have misunderstood, but I didn't get the impression this should be the case from reading: https://en.cppreference.com/w/cpp/language/cv
My best guess is that for some reason, `x` is a constant object only if initialized itself via a constant object. But I couldn't find anything saying that is the case. I asked on the #learn channel of cppslack.
In my example, even though `x` is a `const` local, the assembly still shows that it reloads `x` and adds `1` if we assign to it from a non-const local `y`.
int y = 42;
const int x = y;
is enough, even though `x` is still a `const` local variable.I might be competitive with an LLM + GPUs, but I imagine it'll demolish me on throughput.
Note that you can use things like `pcomplete` and `eshell-syntax-highlighting` to make eshell fairly smart. The fact that my shell can use corfu and the same completion, filtering, and ordering as the rest of my emacs is great.