mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
perf(terrain): evict from the front of the point cache instead of sorting it
Once the cache was full, every write sorted all 50,000 entries to find the oldest. A Map keeps insertion order, so keep the map in fetch-time order and delete from the front: the resolver re-inserts each point it refetches (an overwrite would leave it in its old place), and the disk load sorts once, since a file written before this change, or a migrated raw-batch cache, is not in age order. Suggested by asturksever on #975.
This commit is contained in:
1 parent
626852401a
commit
e60cbb42e3
2 files changed
+17
-5
No files matched your search
@@ -90,15 +90,23 @@ export function terrainHeightsProxy(options = {}) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop the oldest entries until the map is back inside its ceiling.
|
* Drop the oldest entries until the map is back inside its ceiling.
|
||||||
* Oldest-first by fetch time, the same shape `trackBackfillProxies` uses.
|
* The map is kept in fetch-time order (sorted once on load, and the resolver
|
||||||
* Terrain does not move, so evicting an old point costs one refetch, never
|
* re-inserts every point it refetches), so the oldest are at the front and
|
||||||
* a wrong answer.
|
* no write sorts the map. Terrain does not move, so evicting an old point
|
||||||
|
* costs one refetch, never a wrong answer.
|
||||||
*/
|
*/
|
||||||
function evictOverflow() {
|
function evictOverflow() {
|
||||||
|
for (const key of mem.keys()) {
|
||||||
if (mem.size <= CACHE_MAX_ENTRIES) return;
|
if (mem.size <= CACHE_MAX_ENTRIES) return;
|
||||||
|
mem.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Put the map in fetch-time order; a file is not guaranteed to be. */
|
||||||
|
function orderByAge() {
|
||||||
const byAge = [...mem.entries()].sort((a, b) => a[1].at - b[1].at);
|
const byAge = [...mem.entries()].sort((a, b) => a[1].at - b[1].at);
|
||||||
for (let i = 0; i < byAge.length && mem.size > CACHE_MAX_ENTRIES; i += 1)
|
mem.clear();
|
||||||
mem.delete(byAge[i][0]);
|
for (const [key, entry] of byAge) mem.set(key, entry);
|
||||||
}
|
}
|
||||||
/** @type {Map<string, Promise<Array<object>>>} single-flight per missing-point subset. */
|
/** @type {Map<string, Promise<Array<object>>>} single-flight per missing-point subset. */
|
||||||
const inflight = new Map();
|
const inflight = new Map();
|
||||||
@@ -165,6 +173,7 @@ export function terrainHeightsProxy(options = {}) {
|
|||||||
}
|
}
|
||||||
diskDirty = mem.size > 0;
|
diskDirty = mem.size > 0;
|
||||||
}
|
}
|
||||||
|
orderByAge();
|
||||||
evictOverflow();
|
evictOverflow();
|
||||||
} catch {
|
} catch {
|
||||||
/* no disk cache yet */
|
/* no disk cache yet */
|
||||||
|
|||||||
@@ -233,6 +233,9 @@ export async function resolveTerrainHeightRequest({
|
|||||||
for (let i = 0; i < missing.length; i += 1) {
|
for (let i = 0; i < missing.length; i += 1) {
|
||||||
const result = fetched[i];
|
const result = fetched[i];
|
||||||
if (validTerrainResult(result)) {
|
if (validTerrainResult(result)) {
|
||||||
|
// Re-insert rather than overwrite: a Map keeps an existing key in
|
||||||
|
// its old position, and the proxy evicts from the front.
|
||||||
|
cache.delete(missing[i].key);
|
||||||
cache.set(missing[i].key, { at: fetchedAt, result });
|
cache.set(missing[i].key, { at: fetchedAt, result });
|
||||||
cacheChanged = true;
|
cacheChanged = true;
|
||||||
} else if (absentTerrainDatum(result)) {
|
} else if (absentTerrainDatum(result)) {
|
||||||
|
|||||||
Reference in new issue
Block a user