It has a single global mutex over alloc/free paths. It does syscalls underneath that lock (mmap)
Every default malloc implementation worked this way about 12 years ago. Making lots of small allocations, even from multiple threads then blaming the allocator is a losing strategy. An allocator is only going to be able to mitigate the damage to speed and interactivity.
The solution is and always has been to make larger allocations and use those efficiently.
They are just naive loops with nearly no optimization.
The compiler should be able to take something with good access patterns and make something fast, especially out of the basic C functions.
they are the backbone of vast amounts of code
Performance wise it's unlikely C string functions are actually the bottleneck in a program. Maybe for specific programs a naive memory copy function could benefit from AVX instructions.
Real programs have to often do things like allocate memory
"Have to" and "often" are debatable. Any allocations in a hot loop are the very first things that should be optimized away after profiling.
>Performance wise it's unlikely C string functions are actually the bottleneck in a program. Maybe for specific programs a naive memory copy function could benefit from AVX instructions.
Many programs use lots of strings. It tends to become a bottleneck. It also tends to be very difficult to improve because the strings are everywhere in that kind of program, and refactoring to eliminate them is either impossible or very risky.
> Every default malloc implementation worked this way about 12 years ago.
Yes, it is now 12 years later, and memory allocators are better. The memory allocators of that time were also better than the ones 12 years their prior. That's the point. > Making lots of small allocations, even from multiple threads then blaming the allocator is a losing strategy. An allocator is only going to be able to mitigate the damage to speed and interactivity.
It's just a reality that musl is measurably worse at multiple threads allocating even in very polite conditions, because it causes lots of contention. If your program allocates in multiple threads, it is probably going to get slower with musl. If you don't want that, other memory allocators will do great even at high allocation rates with more threads. You could write many other data structures that had equally poor behavior under multi-threaded contention by just throwing a lock around everything and calling it a day, and those bad data structures would also cause "damage to speed and interactivity" or whatever. This isn't very hard to understand. > The compiler should be able to take something with good access patterns and make something fast, especially out of the basic C functions.
I agree, modern compilers are good. But these are extremely common specified functions, they are called everywhere all the time in every C codebase (and that's partially why compilers even recognize these patterns specifically so they can insert optimized routines). Mature implementations that are hand optimized still pay off and also tend to be tuned for various edge cases or quirks that aren't going to come for free from the C compiler either, so it's still work even if you aren't writing assembly for everything or whatever (e.g. uarch dependent codepaths, or optimizations for short strings or whatever).glibc's AVX2 based memcpy functions have a non-negligible performance impact in at least 1 application I maintain on the order of like 8-ish% vs musl (wall clock). It just has to memcpy/memmove a whole lot. Whether or not that's tolerable is up to debate, but a spade is a spade.
> Performance wise it's unlikely C string functions are actually the bottleneck in a program. Maybe for specific programs a naive memory copy function could benefit from AVX instructions.
I said "backbone", not "bottleneck". They are common functions sprinkled in everywhere throughout every application in every codepath on something like a modern Linux desktop. An inverted callstack flamegraph can show you stuff like this. It is basically no different than compiling your application at -O1 and -O2 with GCC. Does the fact your program get 20% faster from -O2 mean that there were "bottlenecks" the compiler solved? No, there was just performance left on the table by emitting better code. > "Have to" and "often" are debatable.
Not really. I have to spell it out apparently: actual programs written by normal human programmers do those things, all the time, they exist in and are common in the world, they depend on other code that does that and is common in the world, they run on your desktop and phone and all servers, and they benefit quite a lot from optimized components like memory allocators and string routines and -O2 making their programs faster. This is pretty easy to observe and the means of doing so should be quite obvious, so there's no real debate.Now whether this fact holds -- whether these programs "have to" do these things or not -- in the imaginary fantasy land people have in their heads where they make up arguments to themselves about how, if every program was written how they liked it, it would be better? That I'm not so sure about, I will admit.
> Every default malloc implementation worked this way about 12 years ago.
Perhaps "default" is doing the heavy lifting here. Glibc malloc was quite bad for a long time, true. But TCmalloc / jemalloc are 21-22 years old, respectively, and jemalloc has been the FreeBSD (released) default malloc implementation for the last 18.
> The solution is and always has been to make larger allocations and use those efficiently.
Having a not-dogshit allocator really doesn't hurt. There's no reason to defend shitty allocator + every application doing manual memory pools on top of it to paper over the bad allocator.