davidtgoldblatt

Joined 15 years ago

Karma
858
Submissions
87
Member since
Feb 24
View on Hacker News

Recent submissions

Comment · 2 years ago
It kinda depends; you only do the membarrier when you're about to sleep anyways, and the non-expedited membarrier() call is just a synchronize_rcu(), so it's not that drastically more expensive than a futex wait.

You don't necessarily want a biased lock for all this kind of stuff, because "sparsely contended" doesn't necessarily imply thread-associated. E.g. one place I was looking at this for was locks for pages of virtual memory in a heap; no thread "owns" any given heap page, but it was very uncommon to get unlucky and have two threads touching adjacent pages at the exact same time. These kind of "sloppy mutexes" get half the fast-path speedup of biased locks but without the heavily asymmetric performance costs. (At least, that was the theory; like I said it didn't really pan out to be that useful in practice).

Comment · 2 years ago
There's two I've tried to do this:

- On the wait side, do the CAS to set the "waiter present" bit. Down unlock, do a (relaxed) read of the lock word, and if "waiter present" isn't set, just do a release store to unlock (and go down some slow CAS-y wake path if a waiter is present). On the wait side, never do an un-timed futex wait; just do a series of timed waits, with increasing wait times (so that you still eventually fix things if you hit the unlucky race between the previous holder's check+store sequence). (You can also do some tricks with counters to let waiters do an unconditional sleep once they get their wait acknowledged).

- Split out the "waiter present" bit into its own byte, do a store-load sequence (with just a compiler reordering fence) to check for waiters, and have waiters either do a membarrier() syscall or wait "long enough" that they're sure they've gotten the same effect. (This gets tricky w.r.t. mutex lifetime though; you either need out of band lifetime knowledge or to use RCU or whatever and indirect through pointers).

Practically, neither was ever "better enough" for anything but microbenchmarks to be worth the complexity.

Comment · 2 years ago
Comment · 4 years ago
> Maybe there is a way to paper over this in the glibc implementation of free_sized (rather than calling free unconditionally), and still do something useful for the glibc allocator. I don't know.

We emailed about this a little contemporaneously (thread "Sized deallocation for C", from February), and I think we came to the conclusion that glibc can make interposition work seamlessly even for interposed allocators lacking free_sized, by checking (in glibc's free_sized) if the glibc malloc/calloc/realloc has been called, and redirecting to free if it hasn't. (The poorly-named "Application Life-Cycle" section of the paper).

Comment · 4 years ago
My usages are similar to yours, but new C standards still benefit me because I can opportunistically detect and make use of new features in a configure script.

To use my baby as an example: free_sized(void *ptr, size_t alloc_size) is new in C23. I can detect whether or not it's available and use it if so. If it's not available, I can just fall back to free() and get the same semantics, at some performance or safety cost.

Comment · 6 years ago
Of particular interest is section 20.18 -- "Mirroring memory operands"; extending (something like) register renaming to memory.
Comment · 6 years ago
> Maybe there's some reason this doesn't work, or maybe the performance improvements aren't worth the effort?

I work on an allocator, and I've lobbied (Linux) kernel people for this over the years. I think everyone mostly agrees it'd be useful (and, the TLB shootdown cost is indeed often nontrivial), but the current architecture of the various subsystem's you'd need to touch makes it hard to do.

As allocators move more and more to a hugepages-first world, I think we'll see fewer and fewer benefits from faster unmappings (instead, we'll just spend more effort making sure we fill in the holes in existing in-use ranges).

Comment · 7 years ago
I can't imagine how pissed off I'd be if I survived a nuclear winter and traveled hundreds of miles to read the prophesied guidestones that could save us all, only to find out that they had several weird eugenicist rules but zero information about like, water purification or animal husbandry.
Comment · 7 years ago
Example usage on some code with interesting pipelining + resource contention properties: https://godbolt.org/z/11oyav

(The code is from the stream vbyte repo; see https://lemire.me/blog/2017/09/27/stream-vbyte-breaking-new-... ).

Edit: Example interesting fact: the first iteration takes 35 cycles before it finishes; but the reciprocal throughput of the loop (assuming it executes a few times) is 5.3 cycles.

Comment · 7 years ago
Depending on what exactly you mean by "hand it a block of memory to manage", you can do this with jemalloc's extent hook functionality. Though that's meant for "I have some long-lived data structures, I want all their memory stored on hugepages" more than "I have this small-ish data structure, I want to batch its allocations together".
Comment · 8 years ago
Oh yes; while certainly mismatched new[]/delete has been a source of problems since forever, what I mean specifically is the new types of exploits possible with the "operator delete(void* ptr, size_t sz)" overload (and its array cousin).

Before C++14, using delete (rather than delete[]) to deallocate an array of ints (or other trivially destructible data types) would be safe in practice, even though it was disallowed. In a world with sized deallocation functions, it's exploitable.

Comment · 8 years ago
Another fun attack vector that I don't think has been well explored yet involves the use of C++ sized deallocation functions. If a base class is missing a virtual destructor, or if an array allocated with new[] is deallocated with delete (instead of delete[]), then the allocation can be freed with an incorrect size parameter. If this happens, you can trigger some of the same sorts of state corruption issues that a double-free would cause (you set a random bitmap bit to "free" in the metadata, since you're calculating offsets from the start of a slab incorrectly).

Valgrind won't ever catch this, and Address Sanitizer won't always (it depends on both the exact type of bug, and the sanitization settings).

Comment · 8 years ago
(As background, I'm a jemalloc developer; reposting my twitter comment on the same article): This is quite a bad way of doing a malloc benchmark -- getting realistic activity patterns is critical (see e.g. Wilson et al.'s survey). It doesn't meaningfully test inter-thread interactions, and randomizes in a way that hurts the effectiveness of thread-local caching.

For the large majority of server workloads on Linux, jemalloc or tcmalloc is probably the right choice of allocator. Trying these out (and spending a few additional test runs tuning their configuration) will often yield significant wins compared to the glibc allocator.

Comment · 8 years ago
It's implementable with a kernel driver on Solaris because of its scheduling hooks. (This was done in the "Mostly Lock-Free Malloc" paper).
Comment · 9 years ago
My experience working on an OSS project has been that the biggest thing missing from the bug-reporter side isn't phrasing or planning, just dedication. If someone doesn't post a useful initial bug report, we can still probably work our way there if they keep pinging the thread or follow whatever instructions come out of the maintenance side of things. OTOH, if I can't see that an issue is actively causing someone pain, it's way easier for it to fall off my radar. Apathy slows down resolution far more often than antipathy.

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton