Compare commits

...
Author SHA1 Message Date
Ludovic MarieandClaude Opus 5.5 2f26231d5c Random ticks: read each chunk's ticked blocks together before ticking them
Each ticking section's block read is a cache miss, waited for one after the
other between the ticks. tick_chunk_random now reads the positions the ticks
will draw first, all together: the misses overlap and the ticks find their
blocks in cache. The positions come from the same Level.randValue sequence,
read without being advanced; the draws, the ticks and their order are
unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 20:09:00 +01:00
2 changed files with 53 additions and 10 deletions

No files matched your search

+13
View File
@@ -1766,6 +1766,19 @@ impl Behaviours {
}
}
}
// The blocks the ticks below read first, read here all together: each
// section's is a cache miss, waited for one after the other between
// the ticks, at once here. The positions are those the ticks draw
// next, the sequence read without being advanced, so nothing changes;
// a tick that changes which sections tick only wastes a read here.
let mut upcoming = ticks.upcoming_random_pos();
let mut from = level.min_y();
while let Some(min_y) = next_ticking(level, from).filter(|&y| y < level.max_y()) {
for _ in 0..speed {
std::hint::black_box(level.block_state(upcoming(min_x, min_y, min_z, 15)));
}
from = min_y + 16;
}
let mut from = level.min_y();
while let Some(min_y) = next_ticking(level, from).filter(|&y| y < level.max_y()) {
for _ in 0..speed {
+40 -10
View File
@@ -168,15 +168,45 @@ impl Ticks {
}
/// `Level.getBlockRandomPos`.
pub fn block_random_pos(&mut self, x: i32, y: i32, z: i32, y_mask: i32) -> BlockPos {
self.random_tick_value = self
.random_tick_value
.wrapping_mul(3)
.wrapping_add(1_013_904_223);
let value = self.random_tick_value >> 2;
BlockPos::new(
x + (value & 15),
y + ((value >> 16) & y_mask),
z + ((value >> 8) & 15),
)
self.random_tick_value = next_random_tick_value(self.random_tick_value);
random_pos(self.random_tick_value, x, y, z, y_mask)
}
/// The positions `block_random_pos` gives next, without drawing them.
pub fn upcoming_random_pos(&self) -> impl FnMut(i32, i32, i32, i32) -> BlockPos + use<> {
let mut value = self.random_tick_value;
move |x, y, z, y_mask| {
value = next_random_tick_value(value);
random_pos(value, x, y, z, y_mask)
}
}
}
fn next_random_tick_value(value: i32) -> i32 {
value.wrapping_mul(3).wrapping_add(1_013_904_223)
}
fn random_pos(value: i32, x: i32, y: i32, z: i32, y_mask: i32) -> BlockPos {
let value = value >> 2;
BlockPos::new(
x + (value & 15),
y + ((value >> 16) & y_mask),
z + ((value >> 8) & 15),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn upcoming_random_positions_are_the_ones_drawn_next() {
let mut ticks = Ticks::new(-123_456_789);
ticks.block_random_pos(0, 0, 0, 15);
let mut upcoming = ticks.upcoming_random_pos();
let ahead: Vec<_> = (0..6)
.map(|i| upcoming(16 * i, -64 + 16 * i, -16 * i, 15))
.collect();
let drawn: Vec<_> = (0..6)
.map(|i| ticks.block_random_pos(16 * i, -64 + 16 * i, -16 * i, 15))
.collect();
assert_eq!(ahead, drawn);
}
}