Faster roof scan and heat pass with identical results (t21)
- The roof is rescanned only in area columns that changed since the last scan: a quiet Huge world goes from 4.60 to 1.88 ms per scan (every 20 ticks). - The heat pass skips react() for solids and powders below their first reaction threshold: Huge world digging 2.54 -> 2.34 ms/tick, quiet 1.39 -> 1.18. - Every suite's output is identical apart from timing lines; seeds make the same worlds. - test/t_sim_optimizations.js checks the dirty roof against a full scan and the heat loop against the reference loop byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// Optimized paths must match the full roof scan and the original heat loop exactly.
|
||||
const fs = require('fs');
|
||||
const { load, suite, flatWorld } = require('./harness');
|
||||
const { run } = load();
|
||||
const { check, done } = suite();
|
||||
run(fs.readFileSync(__dirname + '/fixtures/heat-chunk.js', 'utf8'));
|
||||
|
||||
// Partial areas and changes at area boundaries, including material appearing in empty sky.
|
||||
flatWorld(run, 99, 97, 80);
|
||||
run('RS = 123; bot.x = 45; bot.y = 30;');
|
||||
run(`
|
||||
const originalWeatherTick = weatherTick;
|
||||
weatherTick = function () {
|
||||
if (tick % 20 === 1) {
|
||||
const roof = roofY.slice(); updateRoof();
|
||||
if (!roof.every((v, x) => v === roofY[x])) globalThis.__roofMiss = (globalThis.__roofMiss || 0) + 1;
|
||||
}
|
||||
originalWeatherTick();
|
||||
};
|
||||
`);
|
||||
for (let k = 0; k < 200; k++) run(`
|
||||
setCell((12 + ${k} % 60) * W + (${k} * 31 % W), ${k} % 3 ? WATER : EMPTY, 18, 0);
|
||||
simTick();
|
||||
`);
|
||||
check('scheduled roof matches a full scan at area edges', !run('globalThis.__roofMiss'), run('globalThis.__roofMiss'));
|
||||
run('weatherTick = originalWeatherTick;');
|
||||
|
||||
// Every material, threshold neighbours, fractional temperatures, burning, cold neighbours,
|
||||
// alternating heat scales, and partial areas exercise both random calls and Float32 stores.
|
||||
const snapshot = () => run(`JSON.stringify({
|
||||
arrays: [mat, temp, life, flags, hot, hotLvl, part, digp, seen].map(a => Array.from(new Uint8Array(a.buffer))),
|
||||
RS, stats, EV, brokenMachines
|
||||
})`);
|
||||
const setup = () => {
|
||||
flatWorld(run, 67, 65, 60);
|
||||
run(`{
|
||||
RS = 456; seen.fill(0); EV.length = 0; stats.ignitions = 0; brokenMachines = [];
|
||||
const ts = [-30, -0.5, 0, 0.5, 18, 97, 100, 260, 380, 600, 880, 1085, 1300, 1530, 1900];
|
||||
for (let i = 0; i < N; i++) {
|
||||
const m = (i * 37) % NMAT;
|
||||
mat[i] = m; temp[i] = ts[i % ts.length] + ((i % 3) - 1) * 0.001;
|
||||
life[i] = i % 256; flags[i] = i % 23 === 0 && FUEL[m] ? F_BURN : 0;
|
||||
}
|
||||
hot.fill(6); hot[0] = 0; hotLvl.fill(2); updateRoof();
|
||||
}`);
|
||||
};
|
||||
for (const scale of [1, 4]) {
|
||||
setup();
|
||||
run(`for (let c = 1; c < NCH; c++) referenceHeatChunk(c, ${scale});`);
|
||||
const expected = snapshot();
|
||||
setup();
|
||||
run(`for (let c = 1; c < NCH; c++) heatChunk(c, ${scale});`);
|
||||
check('heat matches original bytes and random state at scale ' + scale, snapshot() === expected);
|
||||
}
|
||||
done();
|
||||
Reference in New Issue
Block a user