Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mimalloc Allocator #643

Merged
merged 735 commits into from
Dec 5, 2022
Merged

Mimalloc Allocator #643

merged 735 commits into from
Dec 5, 2022

Conversation

paigereeves
Copy link
Contributor

@paigereeves paigereeves commented Aug 17, 2022

This pull request introduces an allocator based on Microsoft's mimalloc (https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf) used in a MarkSweep GC. This closes #348.

This PR has a few leftover issues: #688

General changes in this PR (if requested, I can separate these changes to different PRs):

  • destroy_mutator():
    • now takes &mut Mutator instead of Box<Mutator>. The reclamation of the Mutator memory is up to the binding, and allows the binding to copy the Mutator value to their thread local data structure, in which case, Rust cannot reclaim the memory as if it is a Box.
    • now calls each allocator for their allocator-specific on_destroy() behavior.
  • Extract Chunk and ChunkMap from Immix, and make it general for other policies to use.
  • Changes in VMBinding constants:
    • LOG_MIN_ALIGNMENT is removed. We still provide MIN_ALIGNMENT. This avoids confusion that a binding may override one constant but not the other.
    • MAX_ALIGNMENT_SHIFT is removed. We provide MAX_ALIGNMENT for the same reason as above.
    • Add USE_ALLOCATION_OFFSET: bool. If a binding never use allocation offset (i.e. offset === 0), they can set this to false, and MMTk core could use this for some optimizations.
  • Changes in ObjectModel:
    • Add UNIFIED_OBJECT_REFERENCE_ADDRESS: bool to allow a binding to tell us if the object reference uses the same address as its object start and its to_address.
    • Add OBJECT_REF_OFFSET_LOWER_BOUND to allow binding to tell us roughly where the object reference points to (with respect of the allocation address).
    • Changes to related methods to cope with this change.

Mark sweep changes in this PR:

  • add two features for marksweep
    • eager_sweeping: sweep unused memory eagerly in each GC. Without this feature, unused memory is swept when we attempt to allocate from them.
    • malloc_mark_sweep: the same as our previous malloc_mark_sweep. When this is used, the mark sweep plan uses MallocSpace and MallocAllocator.
  • Move the current policy::mallocspace to policy::marksweepspace::malloc_ms
  • Add policy::marksweepspace::native_ms for the mark sweep space with mimalloc.
  • Add util::alloc::free_list_allocator.

@wks wks mentioned this pull request Nov 11, 2022
Comment on lines +344 to +366
impl<VM: VMBinding> GCWork<VM> for SweepChunk<VM> {
#[inline]
fn do_work(&mut self, _worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
debug_assert!(self.space.chunk_map.get(self.chunk) == ChunkState::Allocated);
// number of allocated blocks.
let mut allocated_blocks = 0;
// Iterate over all allocated blocks in this chunk.
for block in self
.chunk
.iter_region::<Block>()
.filter(|block| block.get_state() != BlockState::Unallocated)
{
if !block.attempt_release(self.space) {
// Block is live. Increment the allocated block count.
allocated_blocks += 1;
}
}
// Set this chunk as free if there is not live blocks.
if allocated_blocks == 0 {
self.space.chunk_map.set(self.chunk, ChunkState::Free)
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some radical thoughts about this. Maybe this SweepChunk work packet is completely unnecessary.

If each BlockList is owned by exactly one mutator (or the global MarkSweepSpace), and each Block is owned by exactly one BlockList, then we can traverse all existing blocks simply by traversing all BlockList instances. That means visiting all BlockList in mutators and the lists in MarkSweepSpace::abandoned_lists. Doing so allows blocks to be swept while the current thread has exclusive access to its BlockList, therefore removing the need to have lock() in BlockList completely.

But I prefer doing this under the protection of Rust's ownership model. From the current code, it looks like there is no way for Block instances to be created anywhere else (other than MarkSweepSpace::acquire_block), held anywhere else, or discarded before they are swept. Rust's ownership model can enforce that, but currently our Block type implements Copy, has pointer semantics, and bypasses the ownership model. I elaborated my thoughts here: #696.

qinsoon added a commit that referenced this pull request Nov 25, 2022
This PR removes most of our uses of `ObjectReference::to_address()`, and replace that with `ObjectModel::ref_to_address()`. With this change, the address we get from an object reference is always guaranteed to be within the allocation. Thus changes in #555 are reverted in this PR.

This PR also addresses the comments raised in #643 (comment).

Changes:
* Replace our use of `ObjectReference::to_address()` to `ObjectModel::ref_to_address()`
* Changes to `ObjectReference`:
  * Rename `ObjectReference::to_address()` to `ObjectReference::to_raw_address()`, and make it clear that we should avoid using it.
  * Remove `Address::to_object_reference()`, and add `ObjectReference::from_raw_address()`. Make it clear that we should avoid using it.
* Changes to `ObjectModel`:
  * add `address_to_ref` which does the opposite of `ref_to_address`
  * add `ref_to_header`
  * rename `object_start_ref` to `ref_to_object_start`, to make it consistent with other methods.
* Change `Treadmill` to store `ObjectReference` instead of `Address`. We previously stored object start address in `Treadmill` and we assumed alloc bit is set on the object start address. With changes in this PR, alloc bit is no longer set on object start address. I made changes accordingly.
* Remove code about 'object ref guard' which was used to deal with the case that an object reference (address) may not be in the same chunk as the allocation. That should no longer happen.
* `alloc_bit::is_alloced_object(address)` now returns an `Option<ObjectReference>`. We may consider return `Option<ObjectReference>` with our API `is_mmtk_object()` as well, but i did not include this change in the PR.
@qinsoon
Copy link
Member

qinsoon commented Nov 28, 2022

I believe I have addressed the issues you pointed out. Can you review the PR again and let me know if there are any further changes required? @wks @wenyuzhao

Copy link
Collaborator

@wks wks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly OK. Only some small problems remain.

@wks
Copy link
Collaborator

wks commented Nov 29, 2022

It looks good to me now.

@wks
Copy link
Collaborator

wks commented Nov 30, 2022

I managed to get the mmtk-ruby binding working. However, if I switch from malloc MarkSweep to native MatkSweep, it will crash in post_alloc when allocating new objects after GC because the VO-bit is already set. (FYI, Ruby uses the "is_mmtk_object" feature which turns on the "global_alloc_bit" feature.)

I am still investigating.

@wks
Copy link
Collaborator

wks commented Nov 30, 2022

I think the implementation of bzero_metadata is unsound when we use it to clear a bit range, not whole pages. Log shows that sometimes bzero_metadata will clear 0 bytes when clearing the VO-bits for a 56-byte cell.

[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff1, end: 0xc0804015ff2, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fca8
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fcb0, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff2, end: 0xc0804015ff3, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fce0
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fce8, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff3, end: 0xc0804015ff4, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fd18
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fd20, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff4, end: 0xc0804015ff5, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fd50
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fd58, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff5, end: 0xc0804015ff6, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fd88
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fd90, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff6, end: 0xc0804015ff7, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fdc0
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fdc8, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff7, end: 0xc0804015ff8, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fdf8
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fe00, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff8, end: 0xc0804015ff8, len: 0
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fe30
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fe38, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff8, end: 0xc0804015ff9, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fe68
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fe70, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ff9, end: 0xc0804015ffa, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fea0
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fea8, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ffa, end: 0xc0804015ffb, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057fed8
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057fee0, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ffb, end: 0xc0804015ffc, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057ff10
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057ff18, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ffc, end: 0xc0804015ffd, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057ff48
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057ff50, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ffd, end: 0xc0804015ffe, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057ff80
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057ff88, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015ffe, end: 0xc0804015fff, len: 1
[2022-11-30T09:25:55Z INFO  mmtk::policy::marksweepspace::native_ms::block] clearing alloc bits. cell: 0x2010057ffb8
[2022-11-30T09:25:55Z INFO  mmtk::util::alloc_bit] bzero_alloc_bit start: 0x2010057ffc0, size: 56
[2022-11-30T09:25:55Z INFO  mmtk::util::metadata::side_metadata::global] zeroing: start: 0xc0804015fff, end: 0xc0804015fff, len: 0

bzero_metadata uses memset to set byte ranges. The granularity of VO-bit metadata is one bit per 8 bytes. Then a 56-byte cell corresponds to 7 bits in the VO-bit metadata. As a result, if we use bzero_metadata to clear VO-bits when doing naive_brute_force_sweep, for every 8 consecutive cells, 7 of them will have the start of the VO-bits in one byte, and the end of the VO-bits in another, while 1 of them have both the start and the end of the VO-bits in the same byte. That explains the log.

(FYI, for Ruby, obj.to_raw_address() == ObjectModel::ref_to_address(obj) == ObjectModel::ref_to_object_start(obj) + 8)

I think it is unsound to clear 0 or 1 bytes. It should only clear the bit range the object occupies, which may span multiple bytes.

Copy link
Collaborator

@wks wks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem related to clearing the VO-bit metadata. It may affect JikesRVM as well.

qinsoon added a commit that referenced this pull request Dec 2, 2022
This PR fixes a bug in `bzero_metadata()` discussed in #643 (comment). When the data address range to be bulk zeroed cannot be mapped into whole bytes in the side metadata, the other bits in the bytes which should not be updated will get zeroed unexpectedly.
@qinsoon
Copy link
Member

qinsoon commented Dec 2, 2022

binding-refs
OPENJDK_BINDING_REF=mimalloc-ms-support
JIKESRVM_BINDING_REF=mimalloc-ms-support
V8_BINDING_REF=update-pr-643

@qinsoon qinsoon added the PR-testing Run binding tests for the pull request (deprecated: use PR-extended-testing instead) label Dec 2, 2022
Copy link
Collaborator

@wks wks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

udesou pushed a commit to udesou/mmtk-core that referenced this pull request Dec 5, 2022
This PR fixes a bug in `bzero_metadata()` discussed in mmtk#643 (comment). When the data address range to be bulk zeroed cannot be mapped into whole bytes in the side metadata, the other bits in the bytes which should not be updated will get zeroed unexpectedly.
@qinsoon qinsoon merged commit 6e1b4df into mmtk:master Dec 5, 2022
wenyuzhao pushed a commit to wenyuzhao/mmtk-core that referenced this pull request Mar 20, 2023
This PR addresses the issues we discussed in mmtk#643 (comment). Basically, Rust expects `From<T>` to always succeed, and Rust suggests using `TryFrom<T>` if the conversion may fail. For us, turning an address into a region may fail. So we should not use `From<T>`. And we do not need the error handling with `TryFrom<T>`. Thus we just provide two methods: `Region::from_unaligned_address()` and `Region::from_aligned_address()`.
wenyuzhao pushed a commit to wenyuzhao/mmtk-core that referenced this pull request Mar 20, 2023
This PR removes most of our uses of `ObjectReference::to_address()`, and replace that with `ObjectModel::ref_to_address()`. With this change, the address we get from an object reference is always guaranteed to be within the allocation. Thus changes in mmtk#555 are reverted in this PR.

This PR also addresses the comments raised in mmtk#643 (comment).

Changes:
* Replace our use of `ObjectReference::to_address()` to `ObjectModel::ref_to_address()`
* Changes to `ObjectReference`:
  * Rename `ObjectReference::to_address()` to `ObjectReference::to_raw_address()`, and make it clear that we should avoid using it.
  * Remove `Address::to_object_reference()`, and add `ObjectReference::from_raw_address()`. Make it clear that we should avoid using it.
* Changes to `ObjectModel`:
  * add `address_to_ref` which does the opposite of `ref_to_address`
  * add `ref_to_header`
  * rename `object_start_ref` to `ref_to_object_start`, to make it consistent with other methods.
* Change `Treadmill` to store `ObjectReference` instead of `Address`. We previously stored object start address in `Treadmill` and we assumed alloc bit is set on the object start address. With changes in this PR, alloc bit is no longer set on object start address. I made changes accordingly.
* Remove code about 'object ref guard' which was used to deal with the case that an object reference (address) may not be in the same chunk as the allocation. That should no longer happen.
* `alloc_bit::is_alloced_object(address)` now returns an `Option<ObjectReference>`. We may consider return `Option<ObjectReference>` with our API `is_mmtk_object()` as well, but i did not include this change in the PR.
wenyuzhao pushed a commit to wenyuzhao/mmtk-core that referenced this pull request Mar 20, 2023
This PR fixes a bug in `bzero_metadata()` discussed in mmtk#643 (comment). When the data address range to be bulk zeroed cannot be mapped into whole bytes in the side metadata, the other bits in the bytes which should not be updated will get zeroed unexpectedly.
wenyuzhao pushed a commit to wenyuzhao/mmtk-core that referenced this pull request Mar 20, 2023
This pull request introduces an allocator based on Microsoft's mimalloc (https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf) used in a MarkSweep GC. This closes mmtk#348.

This PR has a few leftover issues: mmtk#688

General changes in this PR (if requested, I can separate these changes to different PRs):
* `destroy_mutator()`:
  * now takes `&mut Mutator` instead of `Box<Mutator>`. The reclamation of the `Mutator` memory is up to the binding, and allows the binding to copy the `Mutator` value to their thread local data structure, in which case, Rust cannot reclaim the memory as if it is a `Box`.
  * now calls each allocator for their allocator-specific `on_destroy()` behavior.
* Extract `Chunk` and `ChunkMap` from Immix, and make it general for other policies to use.
* Changes in `VMBinding` constants:
  * `LOG_MIN_ALIGNMENT` is removed. We still provide `MIN_ALIGNMENT`. This avoids confusion that a binding may override one constant but not the other.
  * `MAX_ALIGNMENT_SHIFT` is removed. We provide `MAX_ALIGNMENT` for the same reason as above.
  * Add `USE_ALLOCATION_OFFSET: bool`. If a binding never use allocation offset (i.e. `offset === 0`), they can set this to `false`, and MMTk core could use this for some optimizations.
* Changes in `ObjectModel`:
  * Add `UNIFIED_OBJECT_REFERENCE_ADDRESS: bool` to allow a binding to tell us if the object reference uses the same address as its object start and its to_address.
  * Add `OBJECT_REF_OFFSET_LOWER_BOUND` to allow binding to tell us roughly where the object reference points to (with respect of the allocation address).
  * Changes to related methods to cope with this change.

Mark sweep changes in this PR:
* add two features for marksweep
  * `eager_sweeping`: sweep unused memory eagerly in each GC. Without this feature, unused memory is swept when we attempt to allocate from them.
  * `malloc_mark_sweep`: the same as our previous `malloc_mark_sweep`. When this is used, the mark sweep plan uses `MallocSpace` and `MallocAllocator`.
* Move the current `policy::mallocspace` to `policy::marksweepspace::malloc_ms`
* Add `policy::marksweepspace::native_ms` for the mark sweep space with mimalloc.
* Add `util::alloc::free_list_allocator`.

Co-authored-by: Yi Lin <qinsoon@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR-testing Run binding tests for the pull request (deprecated: use PR-extended-testing instead)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mimalloc Port (Free List Allocator and Mark Sweep Space)
4 participants