Skip to content

Commit

Permalink
Introduce MockVM (#1049)
Browse files Browse the repository at this point in the history
This PR introduces `MockVM`, a type that implements `VMBinding` and
allows users to control the behavior of each method for testing. This PR
also moves all the tests in the current `DummyVM` to `MockVM`, and
removes the current `DummyVM`.

This PR closes #99. Note that
the current `MockVM` implementation does not allow changing constants or
associated types in `VMBinding` -- I would suggest we create another
issue to track this problem.

Changes:
* Introduce `MockVM`, and write all the current `DummyVM` tests with
`MockVM`.
* Remove `DummyVM`, and remove `./examples` which uses `DummyVM`.
* Change CI scripts to run tests with `MockVM`.
* Remove `pub` visibility for some modules. Those modules were exposed
so we can test them from `DummyVM`. As now `MockVM` is a part of the
main crate, we can test private items. We no longer need `pub` for those
modules.
  • Loading branch information
qinsoon authored Dec 18, 2023
1 parent bf1bad3 commit 658bce8
Show file tree
Hide file tree
Showing 89 changed files with 3,198 additions and 2,856 deletions.
6 changes: 5 additions & 1 deletion .github/scripts/ci-style.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ if [[ $arch == "x86_64" && $os == "linux" ]]; then
cargo clippy --tests --features perf_counter
fi

# mock tests
cargo clippy --features mock_test
cargo clippy --features mock_test --tests
cargo clippy --features mock_test --benches

# --- Check auxiliary crate ---

style_check_auxiliary_crate() {
Expand All @@ -37,4 +42,3 @@ style_check_auxiliary_crate() {
}

style_check_auxiliary_crate macros
style_check_auxiliary_crate vmbindings/dummyvm
22 changes: 8 additions & 14 deletions .github/scripts/ci-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,29 @@ if [[ $arch == "x86_64" && $os == "linux" ]]; then
cargo test --features perf_counter
fi

./examples/build.py

ALL_PLANS=$(sed -n '/enum PlanSelector/,/}/p' src/util/options.rs | sed -e 's;//.*;;g' -e '/^$/d' -e 's/,//g' | xargs | grep -o '{.*}' | grep -o '\w\+')

# Test with DummyVM (each test in a separate run)
cd vmbindings/dummyvm
for fn in $(ls src/tests/*.rs); do
t=$(basename -s .rs $fn)

if [[ $t == "mod" ]]; then
continue
fi
# Test with mock VM:
# - Find all the files that start with mock_test_
# - Run each file separately with cargo test, with the feature 'mock_test'
find ./src ./tests -type f -name "mock_test_*" | while read -r file; do
t=$(basename -s .rs $file)

# Get the required plans.
# Some tests need to be run with multiple plans because
# some bugs can only be reproduced in some plans but not others.
PLANS=$(sed -n 's/^\/\/ *GITHUB-CI: *MMTK_PLAN=//p' $fn)
PLANS=$(sed -n 's/^\/\/ *GITHUB-CI: *MMTK_PLAN=//p' $file)
if [[ $PLANS == 'all' ]]; then
PLANS=$ALL_PLANS
elif [[ -z $PLANS ]]; then
PLANS=NoGC
fi

# Some tests need some features enabled.
FEATURES=$(sed -n 's/^\/\/ *GITHUB-CI: *FEATURES=//p' $fn)
FEATURES=$(sed -n 's/^\/\/ *GITHUB-CI: *FEATURES=//p' $file)

# Run the test with each plan it needs.
for MMTK_PLAN in $PLANS; do
env MMTK_PLAN=$MMTK_PLAN cargo test --features "$FEATURES" -- $t;
env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t;
done
done

9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,26 @@ sysinfo = "0.29"
[dev-dependencies]
paste = "1.0.8"
rand = "0.8.5"
criterion = "0.4"

[build-dependencies]
built = { version = "0.7.1", features = ["git2"] }

[[bench]]
name = "main"
harness = false

[features]
default = []

# This feature is only supported on x86-64 for now
# It's manually added to CI scripts
perf_counter = ["pfm"]

# This feature is only used for tests with MockVM.
# CI scripts run those tests with this feature.
mock_test = []

# .github/scripts/ci-common.sh extracts features from the following part (including from comments).
# So be careful when editing or adding stuff to the section below.

Expand Down
17 changes: 17 additions & 0 deletions benches/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use criterion::Criterion;

use mmtk::memory_manager;
use mmtk::util::test_util::fixtures::*;
use mmtk::AllocationSemantics;

pub fn bench(c: &mut Criterion) {
// Disable GC so we won't trigger GC
let mut fixture = MutatorFixture::create_with_heapsize(1 << 30);
memory_manager::disable_collection(fixture.mmtk());
c.bench_function("alloc", |b| {
b.iter(|| {
let _addr =
memory_manager::alloc(&mut fixture.mutator, 8, 8, 0, AllocationSemantics::Default);
})
});
}
37 changes: 37 additions & 0 deletions benches/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use criterion::criterion_group;
use criterion::criterion_main;
use criterion::Criterion;

// As we can only initialize one MMTk instance, we have to run each benchmark in a separate process.
// So we only register one benchmark to criterion ('bench_main'), and based on the env var MMTK_BENCH,
// we pick the right benchmark to run.

// The benchmark can be executed with the following command. The feature `mock_test` is required, as the tests use MockVM.
// MMTK_BENCH=alloc cargo bench --features mock_test
// MMTK_BENCH=sft cargo bench --features mock_test

// [Yi] I am not sure if these benchmarks are helpful any more after the MockVM refactoring. MockVM is really slow, as it
// is accessed with a lock, and it dispatches every call to function pointers in a struct. These tests may use MockVM,
// so they become slower as well. And the slowdown
// from MockVM may hide the actual performance difference when we change the functions that are benchmarked.
// We may want to improve the MockVM implementation so we can skip dispatching for benchmarking, or introduce another MockVM
// implementation for benchmarking.
// However, I will just keep these benchmarks here. If we find it not useful, and we do not plan to improve MockVM, we can delete
// them.

mod alloc;
mod sft;

fn bench_main(c: &mut Criterion) {
match std::env::var("MMTK_BENCH") {
Ok(bench) => match bench.as_str() {
"alloc" => alloc::bench(c),
"sft" => sft::bench(c),
_ => panic!("Unknown benchmark {:?}", bench),
},
Err(_) => panic!("Need to name a benchmark by the env var MMTK_BENCH"),
}
}

criterion_group!(benches, bench_main);
criterion_main!(benches);
19 changes: 19 additions & 0 deletions benches/sft.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use criterion::black_box;
use criterion::Criterion;

use mmtk::memory_manager;
use mmtk::util::test_util::fixtures::*;
use mmtk::util::test_util::mock_vm::*;
use mmtk::vm::ObjectModel;
use mmtk::vm::VMBinding;
use mmtk::AllocationSemantics;

pub fn bench(c: &mut Criterion) {
let mut fixture = MutatorFixture::create();
let addr = memory_manager::alloc(&mut fixture.mutator, 8, 8, 0, AllocationSemantics::Default);
let obj = <MockVM as VMBinding>::VMObjectModel::address_to_ref(addr);

c.bench_function("sft read", |b| {
b.iter(|| memory_manager::is_in_mmtk_spaces::<MockVM>(black_box(obj)))
});
}
8 changes: 4 additions & 4 deletions docs/userguide/src/portingguide/perf_tuning/alloc.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ If the VM is not implemented in Rust,
the binding needs to turn the boxed pointer into a raw pointer before storing it.

```rust
{{#include ../../../../../vmbindings/dummyvm/src/tests/doc_mutator_storage.rs:mutator_storage_boxed_pointer}}
{{#include ../../../../../src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs:mutator_storage_boxed_pointer}}
```

### Option 2: Embed the `Mutator` struct
Expand All @@ -44,7 +44,7 @@ If the implementation language is not Rust, the developer needs to create a type
have an assertion to ensure that the native type has the exact same layout as the Rust type `Mutator`.

```rust
{{#include ../../../../../vmbindings/dummyvm/src/tests/doc_mutator_storage.rs:mutator_storage_embed_mutator_struct}}
{{#include ../../../../../src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs:mutator_storage_embed_mutator_struct}}
```

### Option 3: Embed the fast-path struct
Expand Down Expand Up @@ -78,7 +78,7 @@ which includes (but not limited to) `NoGC`, `SemiSpace`, `Immix`, generational p
If a plan does not do bump-pointer allocation, we may still implement fast-paths, but we need to embed different data structures instead of `BumpPointer`.

```rust
{{#include ../../../../../vmbindings/dummyvm/src/tests/doc_mutator_storage.rs:mutator_storage_embed_fast-path_struct}}
{{#include ../../../../../src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs:mutator_storage_embed_fastpath_struct}}
```

And pseudo-code for how you would reset the `BumpPointer`s for all mutators in `resume_mutators`. Note that these mutators are the runtime's actual mutator threads (i.e. where the cached bump pointers are stored) and are different from MMTk's `Mutator` struct.
Expand Down Expand Up @@ -120,7 +120,7 @@ Once MMTk is initialized, a binding can get the memory offset for the default al
with the default allocation semantics, we can use the offset to get a reference to the actual allocator (with unsafe code), and allocate with the allocator.

```rust
{{#include ../../../../../vmbindings/dummyvm/src/tests/doc_avoid_resolving_allocator.rs:avoid_resolving_allocator}}
{{#include ../../../../../src/vm/tests/mock_tests/mock_test_doc_avoid_resolving_allocator.rs:avoid_resolving_allocator}}
```

## Emitting Allocation Sequence in a JIT Compiler
Expand Down
17 changes: 0 additions & 17 deletions examples/allocation_benchmark.c

This file was deleted.

23 changes: 0 additions & 23 deletions examples/bench.sh

This file was deleted.

102 changes: 0 additions & 102 deletions examples/build.py

This file was deleted.

27 changes: 0 additions & 27 deletions examples/main.c

This file was deleted.

Loading

0 comments on commit 658bce8

Please sign in to comment.