mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
gex-kernel: zero_dirty scans a page eight bytes per load
hp18's main-Worker profile (HK in the page, 5-65 s) had GuestMem::zero_dirty
at 4.3 s of 60 under sys_mmap: the clean-page test folded bytes one load at a
time (the shim has no SIMD). page_is_zero ORs 64-bit words, four accumulators,
an early out every 256 bytes; same result (test page_is_zero_sees_every_byte).
hp20 (this shim, which also has master 9e5ac0ff, and the UnityPlayer unit; not an
isolated A/B): first frame 52 s, 100 presents 70 s
(hp16 before it: 56 s, 85 s), 41.4 presents/s at the menu.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_1896c5b302a54696aaa92c5f4047f20b
This commit is contained in:
1 parent
0a588c01bd
commit
38739efc77
1 file changed
+36
-4
@@ -177,10 +177,7 @@ impl<'a> GuestMem<'a> {
|
||||
pub fn zero_dirty(&mut self, addr: u64, len: usize) -> Result<(), Errno> {
|
||||
let r = self.range(addr, len)?;
|
||||
for page in self.view_mut()[r].chunks_mut(4096) {
|
||||
// OR-folded 64 bytes at a time, which vectorises: this scan is
|
||||
// the whole cost for the common case of a clean page.
|
||||
let clean = page.chunks(64).all(|c| c.iter().fold(0u8, |a, &b| a | b) == 0);
|
||||
if !clean {
|
||||
if !page_is_zero(page) {
|
||||
page.fill(0);
|
||||
}
|
||||
}
|
||||
@@ -300,6 +297,20 @@ mod tests {
|
||||
|
||||
/// Every byte of the range reads as zero afterwards, whichever pages were
|
||||
/// dirty, including a range that starts and ends mid-page.
|
||||
#[test]
|
||||
fn page_is_zero_sees_every_byte() {
|
||||
let mut buf = vec![0u8; 4096 + 16];
|
||||
for off in [0usize, 1, 3, 8] {
|
||||
let p = &mut buf[off..off + 4096];
|
||||
assert!(super::page_is_zero(p));
|
||||
for i in [0usize, 1, 7, 255, 256, 2049, 4095] {
|
||||
p[i] = 0x80;
|
||||
assert!(!super::page_is_zero(p), "byte {i} at offset {off}");
|
||||
p[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_dirty_zeroes_what_was_written() {
|
||||
let mut buf = vec![0u8; 0x6000];
|
||||
@@ -321,6 +332,27 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether every byte of `b` is zero. The scan is the whole cost of
|
||||
/// [`GuestMem::zero_dirty`] for the common clean page, and a 64-bit build
|
||||
/// for wasm has no SIMD: a byte-wise fold is one load per byte, this is one
|
||||
/// per eight (an unaligned 64-bit load), four running at once, with an early
|
||||
/// out every 256 bytes.
|
||||
fn page_is_zero(b: &[u8]) -> bool {
|
||||
let w = |c: &[u8]| u64::from_ne_bytes(c.try_into().unwrap());
|
||||
let mut blocks = b.chunks_exact(256);
|
||||
let clean = (&mut blocks).all(|c| {
|
||||
let (mut a, mut x, mut y, mut z) = (0u64, 0u64, 0u64, 0u64);
|
||||
for q in c.chunks_exact(32) {
|
||||
a |= w(&q[0..8]);
|
||||
x |= w(&q[8..16]);
|
||||
y |= w(&q[16..24]);
|
||||
z |= w(&q[24..32]);
|
||||
}
|
||||
a | x | y | z == 0
|
||||
});
|
||||
clean && blocks.remainder().iter().all(|&v| v == 0)
|
||||
}
|
||||
|
||||
/// Bumped (after the push, under the kernel lock) whenever code is mapped or
|
||||
/// retired (`Kernel::new_code`, `Kernel::retired_code`): a host checks this
|
||||
/// before taking the kernel lock to drain them, on every syscall.
|
||||
|
||||
Reference in new issue
Block a user