🐛 Performance issues in Academy Overview (GRT audit findings)
Summary
After auditing the GRT codebase, several performance bottlenecks were identified, especially affecting the Academy Overview (_GRCRT_AO.d() and related functions).
With a moderate-to-large dataset (e.g. 100+ towns), these issues cause:
- Excessive CPU usage
- Noticeable UI lag
- Poor scalability due to O(n²) patterns
- Risk of browser freezing in extreme cases
Most problems stem from nested loops, redundant computations, and lack of caching.
🔍 Observed Impact
- Severe slowdown when opening Academy Overview
- CPU spikes due to nested iterations
- Inefficient DOM access patterns
- Performance degradation grows exponentially with data size
📌 Identified Problems & Suggested Fixes
❗ Problem 1 — Nested loop O(n²) in _GRCRT_AO.d() (CRITICAL)
Issue:
For each town in N, the code iterates the entire P collection to find a match by ID.
This results in:
- O(n²) complexity (e.g. 100 towns → 10,000 iterations)
Current code:
$.each(N, function(ia, da) {
P.forEach(function(U) {
if (da.id == U.id) { ... }
});
});
Suggested fix (use hash map for O(1) lookup):
var townMap = {};
P.forEach(function(U) { townMap[U.id] = U; });
$.each(N, function(ia, da) {
var U = townMap[da.id];
if (U) { ... }
});
❗ Problem 2 — A() called inside nested loop (CRITICAL)
Issue:
A() recalculates shared state (Y) and is called:
Inside the nested loop (multiple times)
Again inside k() (indirect duplication)
Current code:
$.each(N, function(ia, da) {
P.forEach(function(U) {
if (da.id == U.id) {
A();
var ua = k(U.id);
}
});
});
Suggested fix (compute once):
A();
$.each(N, function(ia, da) {
var U = townMap[da.id];
if (U) {
var ua = k(U.id); // ensure k() does NOT call A()
}
});
⚠️ Problem 3 — Manual bubble sort O(n²) (HIGH)
Issue:
A custom bubble sort is used instead of native sorting.
Current code:
var X = false;
do {
X = false;
for (var aa = 0; aa < N.length - 1; aa++) {
if (N[aa].name > N[aa + 1].name) {
X = N[aa]; N[aa] = N[aa + 1]; N[aa + 1] = X; X = true;
}
}
} while (X);
Suggested fix:
N.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
⚠️ Problem 4 — Repeated Object.size() in inner loop (HIGH)
Issue:
Object.size() iterates over the object and is called multiple times per iteration.
Current code:
var ba = Y && Y[U.id] && Object.size(Y[U.id]) == queueLen || false;
$.each(GameData.researches, function(Fa, $a) {
var db = Y && Y[U.id] && Object.size(Y[U.id]) == queueLen || false;
});
Suggested fix (cache result):
var queueLen = GameDataConstructionQueue.getResearchOrdersQueueLength();
var ySize = Y && Y[U.id] ? Object.size(Y[U.id]) : 0;
var ba = ySize == queueLen;
$.each(GameData.researches, function(Fa, $a) {
var db = ba;
});
⚠️ Problem 5 — Repeated jQuery selector in R() (HIGH)
Issue:
The same DOM query is executed for every iteration.
Current code:
$.each(Ha[Ua.toString()], function(u, H) {
$("#grcrt_radar_result ul").append(...);
});
Suggested fix (cache selector):
var $ul = $("#grcrt_radar_result ul").html("");
$.each(Ha[Ua.toString()], function(u, H) {
$ul.append(...);
});
⚠️ Problem 6 — ajaxComplete processes all windows on every request (MEDIUM)
Issue:
Runs on every AJAX call and iterates all open windows regardless of relevance.
Current code:
$(document).ajaxComplete(function(d, k, m) {
$.each(Layout.wnd.getAllOpen(), function(p, C) {
...
});
});
Suggested fix (early filtering):
$(document).ajaxComplete(function(d, k, m) {
if (!RepConv.settings[RepConv.Cookie + "_trade"]) return;
var url = m.url;
if (url.indexOf("farm_town") === -1 && url.indexOf("island_info") === -1) return;
$.each(Layout.wnd.getAllOpen(), function(p, C) { ... });
});
⚠️ Problem 7 — Linear search in getPlayerId4Name (MEDIUM)
Issue:
Performs O(n) lookup over all players.
Current code:
this.getPlayerId4Name = function(d) {
var k;
$.each(RepConv.cachePlayers, function(m, p) {
p.name == d && (k = p.id);
});
return k || null;
};
Suggested fix (indexed lookup):
// Build index once
RepConv.cachePlayersByName = {};
$.each(k, function(m, p) {
RepConv.cachePlayersByName[p.name] = p.id;
});
// O(1) lookup
this.getPlayerId4Name = function(d) {
return RepConv.cachePlayersByName[d] || null;
};
🟡 Problem 8 — GetLabel() without memoization (LOW)
Issue:
Repeated string splitting and traversal on every call.
Current code:
this.GetLabel = function(d) {
var k, m = d.split("."), p = RepConv.Lang;
$.each(m, function(C, G) { ... });
return k || this.getLabelLangArray(d);
};
Suggested fix (memoization):
var _labelCache = {};
this.GetLabel = function(d) {
if (_labelCache[d] !== undefined) return _labelCache[d];
var k, m = d.split("."), p = RepConv.Lang;
$.each(m, function(C, G) { ... });
return (_labelCache[d] = k || this.getLabelLangArray(d));
};
The most impactful issues (1 & 2) introduce quadratic complexity in critical rendering paths, especially when opening the Academy Overview.
Combined with redundant computations and DOM inefficiencies, these problems significantly affect performance and scalability.
🚀 Expected Outcome After Fixes
Reduced time complexity from O(n²) to O(n) in key paths
Lower CPU usage during rendering
Faster UI response times
Improved scalability for large accounts
Elimination of major browser slowdowns/freezes
🐛 Performance issues in Academy Overview (GRT audit findings)
Summary
After auditing the GRT codebase, several performance bottlenecks were identified, especially affecting the Academy Overview (
_GRCRT_AO.d()and related functions).With a moderate-to-large dataset (e.g. 100+ towns), these issues cause:
Most problems stem from nested loops, redundant computations, and lack of caching.
🔍 Observed Impact
📌 Identified Problems & Suggested Fixes
❗ Problem 1 — Nested loop O(n²) in
_GRCRT_AO.d()(CRITICAL)Issue:
For each town in
N, the code iterates the entirePcollection to find a match by ID.This results in:
Current code:
Suggested fix (use hash map for O(1) lookup):
❗ Problem 2 — A() called inside nested loop (CRITICAL)
Issue:
A() recalculates shared state (Y) and is called:
Inside the nested loop (multiple times)
Again inside k() (indirect duplication)
Current code:
Suggested fix (compute once):
Issue:
A custom bubble sort is used instead of native sorting.
Current code:
Suggested fix:
Issue:
Object.size() iterates over the object and is called multiple times per iteration.
Current code:
Suggested fix (cache result):
Issue:
The same DOM query is executed for every iteration.
Current code:
Suggested fix (cache selector):
Issue:
Runs on every AJAX call and iterates all open windows regardless of relevance.
Current code:
Suggested fix (early filtering):
Issue:
Performs O(n) lookup over all players.
Current code:
Suggested fix (indexed lookup):
🟡 Problem 8 — GetLabel() without memoization (LOW)
Issue:
Repeated string splitting and traversal on every call.
Current code:
Suggested fix (memoization):
The most impactful issues (1 & 2) introduce quadratic complexity in critical rendering paths, especially when opening the Academy Overview.
Combined with redundant computations and DOM inefficiencies, these problems significantly affect performance and scalability.
🚀 Expected Outcome After Fixes
Reduced time complexity from O(n²) to O(n) in key paths
Lower CPU usage during rendering
Faster UI response times
Improved scalability for large accounts
Elimination of major browser slowdowns/freezes