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:
@@ -186,6 +186,7 @@ function simMove() {
|
||||
for (let k = 0; k < CW; k++) {
|
||||
const cx = ltr ? k : CW - 1 - k;
|
||||
if (!awake[rowBase + cx]) continue;
|
||||
roofDirty[cx] = 1;
|
||||
const x0 = cx << 5, x1 = Math.min(W, x0 + 32);
|
||||
// alternate rows scan left to right and right to left, so nothing drifts one way
|
||||
const dx = ltr ? 1 : -1, xEnd = ltr ? x1 : x0 - 1;
|
||||
|
||||
+20
-1
@@ -7,6 +7,20 @@ function airflowAt(i) { const a = airS[i]; return a !== 0 && ((curAir() - a) & 0
|
||||
const POROUS = new Uint8Array(NMAT); POROUS[ASH] = 1; POROUS[SLAG] = 1; POROUS[COAL] = 1; POROUS[CHARCOAL] = 1;
|
||||
const SOIL = new Uint8Array(NMAT); SOIL[DIRT] = 1; SOIL[GRASS] = 1; SOIL[LDIRT] = 1;
|
||||
const HOTLIQ = new Uint8Array(NMAT); for (const m of [MAGMA, MCU, MFE, MST, MGL, MAG, MAU, MBZ, MBS, MSLAG]) HOTLIQ[m] = 1;
|
||||
// Cold solids and powders cannot react below their first applicable threshold.
|
||||
const REACT_MIN = new Float64Array(NMAT).fill(Infinity);
|
||||
for (let m = 0; m < NMAT; m++) {
|
||||
if (PH[m] === PH_LIQUID || PH[m] === PH_GAS) REACT_MIN[m] = -Infinity;
|
||||
else {
|
||||
if (IGN[m] !== 0) REACT_MIN[m] = Math.min(REACT_MIN[m], IGN[m]);
|
||||
if (MELT[m] !== 0) REACT_MIN[m] = Math.min(REACT_MIN[m], MELT[m]);
|
||||
}
|
||||
}
|
||||
REACT_MIN[CLAY] = Math.min(REACT_MIN[CLAY], 880);
|
||||
REACT_MIN[LCLAY] = Math.min(REACT_MIN[LCLAY], 880);
|
||||
REACT_MIN[WOOD] = Math.min(REACT_MIN[WOOD], 380);
|
||||
REACT_MIN[LWOOD] = Math.min(REACT_MIN[LWOOD], 380);
|
||||
REACT_MIN[GRASS] = Math.min(REACT_MIN[GRASS], 260);
|
||||
// Alloys: a molten additive grain turns neighbouring molten copper into the alloy,
|
||||
// and is itself used up after about this many (the ratio: bronze ~1:7 tin, brass ~1:2 zinc).
|
||||
const ALLOY = { [MSN]: { into: MBZ, per: 7 }, [MZN]: { into: MBS, per: 2 } };
|
||||
@@ -51,6 +65,7 @@ function heatPass() {
|
||||
function heatChunk(c, sc) {
|
||||
const cx = c % CW, cy = (c / CW) | 0;
|
||||
const x0 = cx << 5, y0 = cy << 5, x1 = Math.min(W, x0 + 32), y1 = Math.min(H, y0 + 32);
|
||||
roofDirty[cx] = 1;
|
||||
const leftCold = cx > 0 && !hot[c - 1], upCold = cy > 0 && !hot[c - CW];
|
||||
let maxDev = 0, burning = false;
|
||||
for (let y = y0; y < y1; y++) {
|
||||
@@ -80,7 +95,11 @@ function heatChunk(c, sc) {
|
||||
} else rel = RELAX[m];
|
||||
t += (amb - t) * rel * sc;
|
||||
temp[i] = t;
|
||||
if (m !== EMPTY) { react(i, x, y, m); m = mat[i]; t = temp[i]; }
|
||||
if (m !== EMPTY) {
|
||||
// Test the stored Float32 temperature, just as react() reads it.
|
||||
if ((flags[i] & F_BURN) || !(temp[i] < REACT_MIN[m])) react(i, x, y, m);
|
||||
m = mat[i]; t = temp[i];
|
||||
}
|
||||
const dv = t > amb ? t - amb : amb - t;
|
||||
if (dv > maxDev) maxDev = dv;
|
||||
if (flags[i] & F_BURN) burning = true;
|
||||
|
||||
+12
-1
@@ -35,7 +35,18 @@ function checkLedger() {
|
||||
|
||||
// Topmost non-air cell of each column: air above it can shed heat to the sky.
|
||||
function updateRoof() {
|
||||
for (let x = 0; x < W; x++) {
|
||||
scanRoof(0, W);
|
||||
roofDirty.fill(0);
|
||||
}
|
||||
function updateDirtyRoof() {
|
||||
for (let cx = 0; cx < CW; cx++) {
|
||||
if (!roofDirty[cx]) continue;
|
||||
scanRoof(cx << 5, Math.min(W, (cx + 1) << 5));
|
||||
roofDirty[cx] = 0;
|
||||
}
|
||||
}
|
||||
function scanRoof(x0, x1) {
|
||||
for (let x = x0; x < x1; x++) {
|
||||
let y = 0;
|
||||
for (; y < H; y++) { const ph = PH[mat[y * W + x]]; if (ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) break; }
|
||||
roofY[x] = y;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
function simTick() {
|
||||
tick++;
|
||||
tickStamp = (tickStamp % 255) + 1;
|
||||
if (tick % 20 === 1) updateRoof();
|
||||
if (tick % 20 === 1) updateDirtyRoof();
|
||||
weatherTick();
|
||||
machinesTick();
|
||||
pipesTick();
|
||||
|
||||
+4
-1
@@ -13,7 +13,7 @@ let ents = []; // loose objects (bars)
|
||||
let bgm; // background layer: trees. Liquids, powders and gases pass in front of it.
|
||||
let flyers = []; // grains in flight, blown out of the bot
|
||||
let ghostBlocks = new Map(), ghostMolds = [];
|
||||
let awake, hot, hotLvl, powderArea;
|
||||
let awake, hot, hotLvl, powderArea, roofDirty;
|
||||
let tick = 0, tickStamp = 1;
|
||||
let machines = [null], molds = [], nets = [null], pcells = new Set(), powerDirty = true;
|
||||
let beltL = [], beltR = [], tubeLists = [[], [], [], [], []];
|
||||
@@ -50,6 +50,7 @@ function alloc(w, h) {
|
||||
surfY = new Int16Array(w); roofY = new Int16Array(w); dirtY = new Int16Array(w); graniteY = new Int16Array(w); basaltY = new Int16Array(w); magmaY = new Int16Array(w);
|
||||
AMB = new Float32Array(h);
|
||||
awake = new Uint8Array(NCH); hot = new Uint8Array(NCH); hotLvl = new Uint8Array(NCH);
|
||||
roofDirty = new Uint8Array(CW).fill(1);
|
||||
powderArea = new Uint8Array(NCH).fill(1); // unknown areas get one visit before being ruled out
|
||||
machines = [null]; molds = []; nets = [null]; pcells = new Set(); powerDirty = true;
|
||||
beltL = []; beltR = []; tubeLists = [[], [], [], [], []]; brokenMachines = [];
|
||||
@@ -85,6 +86,7 @@ function fillNoise() {
|
||||
// ---------- Waking ----------
|
||||
function wakeXY(x, y) {
|
||||
const cx = x >> 5, cy = y >> 5, c = cy * CW + cx;
|
||||
roofDirty[cx] = 1;
|
||||
awake[c] = 3;
|
||||
const lx = x & 31, ly = y & 31;
|
||||
if (lx === 0) { if (cx > 0) awake[c - 1] = 3; } else if (lx === 31 && cx < CW - 1) awake[c + 1] = 3;
|
||||
@@ -92,6 +94,7 @@ function wakeXY(x, y) {
|
||||
}
|
||||
function wakeHeatXY(x, y) {
|
||||
const c = (y >> 5) * CW + (x >> 5);
|
||||
roofDirty[x >> 5] = 1;
|
||||
hot[c] = 6; hotLvl[c] = 2;
|
||||
}
|
||||
function wakeIdx(i) { wakeXY(i % W, (i / W) | 0); }
|
||||
|
||||
Reference in New Issue
Block a user