diff options
| author | user <user@clank> | 2026-08-07 03:28:55 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-07 03:28:55 +0200 |
| commit | 98b3b98eb1bf8f195ef4ebb1375c9f161d758c17 (patch) | |
| tree | 810ec6ef2e88453a9fc076216dbc8a9b7e797a45 /idatui/graph.py | |
| parent | Baseline for the v5 bench (landing polls every 2ms instead of 10ms; the poll ... (diff) | |
| download | ida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.tar.gz ida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.tar.xz ida-tui-98b3b98eb1bf8f195ef4ebb1375c9f161d758c17.zip | |
Three targeted cuts: the graph's transposition pass counts keep and swap in one pass over the neighbour pairs (was four _pair_cross calls); the barycentre median answers degree 1 and 2 without sorting; and the search body is built from windowed model reads instead of one locked row lookup per line.
Result: {"status":"keep","total_ms":17944.4,"lg_boot_ms":703.7,"lg_decomp_ms":2453.7,"lg_graph_ms":1000,"lg_hex_ms":478,"lg_index_ms":96,"lg_listing_cold_ms":453,"lg_listing_warm_ms":411.2,"lg_nav_ms":6555.9,"lg_palette_ms":4.9,"lg_render_ms":218.4,"lg_search_ms":1308.8,"pure_graph_ms":212.3,"sm_boot_ms":432.4,"sm_decomp_ms":1267.8,"sm_graph_ms":742.7,"sm_hex_ms":440,"sm_index_ms":2.5,"sm_listing_cold_ms":264.3,"sm_listing_warm_ms":289.5,"sm_nav_ms":305.4,"sm_palette_ms":0.3,"sm_render_ms":258.6,"sm_search_ms":45,"fails":0}
Diffstat (limited to 'idatui/graph.py')
| -rw-r--r-- | idatui/graph.py | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/idatui/graph.py b/idatui/graph.py index e0a9422..baee597 100644 --- a/idatui/graph.py +++ b/idatui/graph.py @@ -280,6 +280,33 @@ def _pair_cross(a: int, b: int, side: dict[int, list[int]], return n +def _swap_delta(a: int, b: int, down: dict[int, list[int]], + up: dict[int, list[int]], pos: dict[int, int]) -> tuple[int, int]: + """``(keep, swap)`` for the adjacent pair (a, b), both sides, in one pass. + + The same as calling :func:`_pair_cross` four times, which is what the + transposition loop used to do: every neighbour pair was visited twice (once + per direction) and each visit was a python call. Counting both outcomes + while the pair is in hand halves the comparisons and removes three calls per + candidate swap — and this runs a third of a million times over a corpus. + """ + keep = swap = 0 + for side in (down, up): + va = side[a] + vb = side[b] + if not va or not vb: + continue + pbs = [pos[v] for v in vb] + for u in va: + pu = pos[u] + for pv in pbs: + if pu > pv: + keep += 1 + elif pu < pv: + swap += 1 + return keep, swap + + def crossings(layers: list[list[int]], down: dict[int, list[int]], pos: dict[int, int]) -> int: return sum(_cross_below(l, down, pos) for l in layers) @@ -306,11 +333,20 @@ def _order_layers(g: _Graph, root: int, sweeps: int = 6) -> list[list[int]]: pos = {i: k for layer in layers for k, i in enumerate(layer)} def median(i: int, side: dict[int, list[int]]) -> float: - ps = sorted(pos[j] for j in side[i]) - if not ps: + # Almost every node in a control-flow graph has one or two neighbours + # on a given side, so answer those without building and sorting a list: + # this runs tens of thousands of times per corpus layout. + js = side[i] + n = len(js) + if n == 1: + return float(pos[js[0]]) + if n == 2: + return (pos[js[0]] + pos[js[1]]) / 2 + if not n: return -1.0 - m = len(ps) // 2 - return float(ps[m]) if len(ps) % 2 else (ps[m - 1] + ps[m]) / 2 + ps = sorted(pos[j] for j in js) + m = n // 2 + return float(ps[m]) if n % 2 else (ps[m - 1] + ps[m]) / 2 best, best_x = [list(l) for l in layers], crossings(layers, down, pos) for s in range(sweeps): @@ -327,8 +363,7 @@ def _order_layers(g: _Graph, root: int, sweeps: int = 6) -> list[list[int]]: for layer in layers: for k in range(len(layer) - 1): a, b = layer[k], layer[k + 1] - keep = _pair_cross(a, b, down, pos) + _pair_cross(a, b, up, pos) - swap = _pair_cross(b, a, down, pos) + _pair_cross(b, a, up, pos) + keep, swap = _swap_delta(a, b, down, up, pos) if swap < keep: layer[k], layer[k + 1] = b, a pos[a], pos[b] = k + 1, k |
