diff options
| author | user <user@clank> | 2026-08-07 08:17:07 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 08:17:07 +0200 |
| commit | b6646aa1c77d3244ec74cb525e7808c8dd8c153b (patch) | |
| tree | e2600a3692f01dd0086b45305c55febc47145cff /idatui | |
| parent | An item edit (c/d/u/p) keeps the listing's walk in front of it instead of dis... (diff) | |
| download | ida-tui-b6646aa1c77d3244ec74cb525e7808c8dd8c153b.tar.gz ida-tui-b6646aa1c77d3244ec74cb525e7808c8dd8c153b.tar.xz ida-tui-b6646aa1c77d3244ec74cb525e7808c8dd8c153b.zip | |
The page-freshness check carries the digest the client already holds (heads(expect=...)) instead of asking first and fetching afterwards. A page that has NOT changed costs one round trip as before; a page that HAS changed now costs one instead of two. Also corrects the record: the item-edit bench hang is pilot start-up flakiness, not the _prime/_grow concurrency I blamed it on — proved with a stack dump.
Result: {"status":"keep","total_ms":25121.9,"lg_boot_ms":694.8,"lg_decomp_ms":2370.8,"lg_graph_ms":917.1,"lg_hex_ms":456.1,"lg_index_ms":69.4,"lg_listing_cold_ms":442.7,"lg_listing_warm_ms":498.8,"lg_nav_ms":6548.8,"lg_palette_ms":4.8,"lg_rename_ms":711.4,"lg_render_ms":224.9,"lg_search_ms":3484.7,"lg_split_ms":2619.3,"pure_graph_ms":220.7,"sm_boot_ms":448.6,"sm_decomp_ms":1292.5,"sm_graph_ms":748.8,"sm_hex_ms":438.3,"sm_index_ms":2.4,"sm_listing_cold_ms":274.3,"sm_listing_warm_ms":266.5,"sm_nav_ms":302.7,"sm_palette_ms":0.3,"sm_rename_ms":392.5,"sm_render_ms":260.2,"sm_search_ms":59.6,"sm_split_ms":1370.9,"fails":0}
Diffstat (limited to 'idatui')
| -rw-r--r-- | idatui/domain.py | 36 |
1 files changed, 16 insertions, 20 deletions
diff --git a/idatui/domain.py b/idatui/domain.py index 2cfd9d9..aedade9 100644 --- a/idatui/domain.py +++ b/idatui/domain.py @@ -1011,30 +1011,26 @@ class ListingModel: want_digest = self._page_digest[p] want_rows = self._page_rows[p] want = [(h.ea, h.kind) for h in self._heads[lo:hi]] - # Ask whether the page still renders as the client holds it. The worker - # builds the rows either way (there is no knowing a line is unchanged - # without rendering it), but skipping the pickling, the transfer, the - # unpickling and the Head rebuild is about 40% of what a page costs -- - # and after a rename almost every page is unchanged. - if want_digest is not None: - try: - probe = self._prog.client.call( - "heads", addr=hex(addr), count=self.PAGE, annotate=True, - digest=True) - except Exception: # noqa: BLE001 -- an older worker has no digest - probe = None - if (isinstance(probe, dict) and probe.get("digest") == want_digest - and probe.get("count") == want_rows): - with self._lock: - if self._text_gen == gen and len(self._heads) >= hi: - for k in range(lo, hi): - self._head_gen[k] = gen - return p + 1 + # Tell the worker what we already hold. It builds the rows either way + # (there is no knowing a line is unchanged without rendering it), but if + # they still hash to the same value it keeps them: the pickling, the + # transfer, the unpickling and the Head rebuild are about 40% of what a + # page costs, and after a rename almost every page is unchanged. Sending + # the expectation rather than asking first means a page that HAS changed + # still costs one round trip. try: payload = self._prog.client.call( - "heads", addr=hex(addr), count=self.PAGE, annotate=True) + "heads", addr=hex(addr), count=self.PAGE, annotate=True, + expect="" if want_digest is None else str(want_digest)) except Exception: # noqa: BLE001 -- keep the old text rather than blank return p + 1 + if (isinstance(payload, dict) and "heads" not in payload + and payload.get("count") == want_rows): + with self._lock: + if self._text_gen == gen and len(self._heads) >= hi: + for k in range(lo, hi): + self._head_gen[k] = gen + return p + 1 rows = payload.get("heads", []) if isinstance(payload, dict) else [] page = self._build_page(rows) with self._lock: |
