FEXCore: Adds a lock-free atomic bitset that supports contiguous range allocations - #5793
Merged
Conversation
Sonicadvance1
force-pushed
the
193
branch
5 times, most recently
from
August 3, 2026 23:55
a42f8fc to
f2b4754
Compare
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 7, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 7, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 7, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 7, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 8, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 8, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
lioncash
reviewed
Aug 10, 2026
lioncash
left a comment
Contributor
There was a problem hiding this comment.
As far as I can tell, this looks good, aside from a few nits. Lot to wrap around on a first read
| return allocate_inside_word(count, last_allocation_track.get_last_allocation(), words_to_track); | ||
| } | ||
|
|
||
| // TODO: Always scans from beginning to end. |
Contributor
There was a problem hiding this comment.
Is this intended to be done in this PR, or a follow-up?
Member
Author
There was a problem hiding this comment.
Planning on a follow up there. Needs some investigation in to if scanning from the start is better at removing sparsity for large allocations, or if we can just eat it.
Useful for removing integer division instructions when we know the source value is aligned to be power of two. As integer division is quite slow, we want to use this when possible.
…e allocations
This thing is a bit intense, so some requirements from the start:
- It needs to be lock-free and thread-safe
- It needs to support contiguous range allocations
- It needs to support allocations larger than a single atomic word
These requirements kind of fly in the face of most bitset allocators
where they will support some parts of these requirements, or just throw
a mutex in front of the whole thing.
Some implementation details:
- If allocating only 1-bit, trivial and always succeeds if there is space
- If allocating <= 64-bit, then always succeeds if there is at least
those many contiguous bits within a single atomic word
- Allocation can fail if there are cross-word contiguous bits of the
size available
- Introduces some sparsity
- If allocating > 64-bits then it falls down the longer scan path.
- Searches for contiguous bits of free space between multiple atomic
words.
- If found, will attempt to allocate tracking which bits were allocated
- If allocation fails, unwind bits already acquired and continue
scanning
Some downsides to this implementation:
- Allocations can fail if sparsity builds up
- Heavily contended allocations can be worse than a lock
- If larger than atomic word allocations are in flight.
- Unwinding larger than word allocations and continuing scanning adds
overhead, a lock would have won at that point.
- A small bit of false sharing where an atomic word is read without
acquire semantics for scanning can technically overlook some
allocations that no longer exist.
- Slower than a linear allocator, but that's not unexpected.
Most of these downsides are okay for our use case, which is code buffer
allocations with the ability to do partial invalidation. If the atomic
bitset fails to fit an allocation, we can throw away the code buffer
like we currently do.
The bitmap allocator that uses this lock-free atomic bitset is still
in-flight but this is one complex container that can land independently.
Hammers the API in a couple of ways to make sure it works.
lioncash
approved these changes
Aug 10, 2026
Sonicadvance1
added a commit
to Sonicadvance1/FEX
that referenced
this pull request
Aug 10, 2026
On top of FEX-Emu#5793 This is tailored towards our needs for our JIT and eventually replacing the linear allocator. Allowing us to reallocate memory for code blocks that have been invalidated, letting us keep a single code buffer around for longer and using less memory overall. In particular, high-invalidation games that ship anti-tamper tend to emit millions of ~128-byte blocks in just a handful of minutes which causes our current linear allocator to consume gigabytes very quickly. This will allow us to more aggressively reuse the allocation space and reduce the memory load in those situations. There's some additional resize tuning that needs some work whence it is in situ which doesn't need to be done now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This thing is a bit intense, so some requirements from the start:
These requirements kind of fly in the face of most bitset allocators
where they will support some parts of these requirements, or just throw
a mutex in front of the whole thing.
Some implementation details:
those many contiguous bits within a single atomic word
size available
words.
scanning
Some downsides to this implementation:
overhead, a lock would have won at that point.
acquire semantics for scanning can technically overlook some
allocations that no longer exist.
Most of these downsides are okay for our use case, which is code buffer
allocations with the ability to do partial invalidation. If the atomic
bitset fails to fit an allocation, we can throw away the code buffer
like we currently do.
The bitmap allocator that uses this lock-free atomic bitset is still
in-flight but this is one complex container that can land independently.