aboutsummaryrefslogtreecommitdiffstats
path: root/idatui/domain.py
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-08-10 00:40:02 +0200
committerblasty <blasty@local>2026-08-10 00:40:28 +0200
commit528f4fe1d4877b24208e9bd87ee15d1ac8a363ed (patch)
tree63913ca0b2a4db6e7b2d9083cc7781d1dbfb539f /idatui/domain.py
parentremote_tools: item class by mask instead of is_code/is_data calls (diff)
downloadida-tui-528f4fe1d4877b24208e9bd87ee15d1ac8a363ed.tar.gz
ida-tui-528f4fe1d4877b24208e9bd87ee15d1ac8a363ed.tar.xz
ida-tui-528f4fe1d4877b24208e9bd87ee15d1ac8a363ed.zip
domain: build Head positionally, and drop a dead test from _as_int
Head is the most-constructed object in the codebase -- 227k of them to stream one bash -- and from_raw was paying for two things it did not need. _as_int ended in a ternary whose arms were BOTH int(v, 16), so the isinstance+startswith in front of them decided nothing and ran on every address the client parses (55k times per 60 pages). Removed; the function is now the isinstance it always was. Head(...) was built by keyword, which makes the tuple match names against fields; positional is the same object with none of that. Address conversion is inlined for the same reason from_raw exists at all. Together 30% off Head construction (2.27 -> 1.59ms per 2003 rows, 0.34us a row), with output verified identical against the previous implementation over both skeleton and full rows. isinstance was kept over the 5%-faster "type(v) is int" because the two differ for bool and int subclasses, and that is not a trade worth making for 5% of 12% of a page. End to end this is ~2.5% of bash's boot (3117 -> 3065ms): Head construction was ~12% of the client half, and the client half is ~54% of what is left. Adds experiments/profile_client.py -- profile_remote.py's counterpart for the half of a page load that happens outside the database process, which is where the remaining time now is. Full gate: 1050 passed.
Diffstat (limited to 'idatui/domain.py')
-rw-r--r--idatui/domain.py28
1 files changed, 19 insertions, 9 deletions
diff --git a/idatui/domain.py b/idatui/domain.py
index 903611f..60dbbbf 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -45,9 +45,12 @@ _TRUNC_RE = re.compile(r"\[(\d+) chars total\]\s*$")
# Value models
# --------------------------------------------------------------------------- #
def _as_int(v) -> int:
+ # Both arms of the ternary this used to end with were `int(v, 16)`, so the
+ # isinstance+startswith test in front of them decided nothing and ran on
+ # every address the client parses -- 55k times per 60 listing pages.
if isinstance(v, int):
return v
- return int(v, 16) if isinstance(v, str) and v.startswith("0x") else int(v, 16)
+ return int(v, 16)
@dataclass(frozen=True)
@@ -140,15 +143,22 @@ class Head(NamedTuple):
# tool emits [str, str] and [int, int, int], so re-coercing them was
# re-proving that once per listing row -- and copying them into tuples
# destroyed the sharing the worker's line cache had just created.
+ #
+ # Built POSITIONALLY, and with the address converted inline. This is the
+ # most-constructed object in the codebase (227k of them to stream one
+ # bash) and the two together are worth ~40%: keyword construction has to
+ # match names against the tuple's fields, and _as_int was a call per row
+ # to do one isinstance and an int().
+ v = d["ea"]
return cls(
- ea=_as_int(d["ea"]),
- kind=d.get("kind", "unknown"),
- size=int(d.get("size", 0) or 0),
- text=d.get("text", ""),
- name=d.get("name"),
- raw=raw,
- spans=d.get("spans") or None,
- ops=d.get("ops") or None,
+ v if isinstance(v, int) else int(v, 16),
+ d.get("kind", "unknown"),
+ int(d.get("size", 0) or 0),
+ d.get("text", ""),
+ d.get("name"),
+ raw,
+ d.get("spans") or None,
+ d.get("ops") or None,
)