Repository navigation
Conversation
lacraig2
force-pushed
the
lookup-cost
branch
3 times, most recently
from
November 13, 2026 23:17
8dcd141 to
825fef5
Compare
… filled tb_lookup's jump-cache hit path is upstream's again. The check that a block bound to a virtual address is used only at that address moves to where a block found in the hash table is written into the jump cache, so a hit costs nothing extra: about 2 host instructions per lookup, 60 to 90 per guest system call.
helper_lookup_tb_ptr and the CPU loop called tcg_hooks_cflags() before every lookup. tb_lookup() now asks for the page's cflags only when the jump cache misses, and looks again with them; the paths that translate apply them before tb_gen_code. The miss path is a function of its own, out of line, so the hit path stays inlined into its callers as upstream's is. A hooks commit empties every vCPU's jump cache, so a hit never serves a block translated before its page gained a hook site. A hit on a page with no hook sites costs what upstream's does.
…op check panda_do_flush_tb() set a loop bit that every CPU-loop iteration tested. It now queues the flush as exclusive work on the requesting vCPU (from outside a vCPU, on the first) and asks every vCPU to leave its TBs. The flush still comes before any more guest code runs, a requesting vCPU runs none until a flush that follows its request, and the loop tests nothing for it. A work item finding the flush already done by another does nothing. PANDA_LOOP_FLUSH is gone; the loop's remaining PANDA bit is the before_handle_exception gate.
With two vCPUs, vCPU 0 asks for a flush from an insn_exec callback and stays in it, so its flush cannot run yet; vCPU 1 then changes what is instrumented and asks for a flush too. The test checks that vCPU 1 runs no more guest code until a flush, and that its next block is translated again (insn_exec fires at the newly instrumented address). Runs on a mkbusybox.py image booted with -smp 2; guest programs for x86_64, aarch64 and riscv64.
lacraig2
force-pushed
the
lookup-cost
branch
from
November 14, 2026 01:16
825fef5 to
38aed84
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The engine adds work to QEMU's TB lookup fast path and CPU loop that every run pays, with nothing subscribed. This moves each piece off the hit path, so a run that uses no address hooks and requests no flushes looks up blocks at upstream's cost.
Changes
pc_boundcheck for aCF_PCRELblock bound to its virtual address moves fromtb_lookup's jump-cache hit path to where a block from the hash table is written into the jump cache.tb_lookup()asks for the page's hook cflags (tcg_hooks_cflags()) only after a jump-cache miss, then looks again.helper_lookup_tb_ptrand the CPU loop no longer call it, and the paths that translate apply it beforetb_gen_code.accel/tcg/hooks.c), so a hit never serves a block translated before its page gained a hook site.panda_do_flush_tb()queues the flush as exclusive work on the requesting vCPU (from outside a vCPU, on the first) and asks every vCPU to leave its TBs, instead of setting a bit the CPU loop tests on every iteration. Every requester queues its own work item, so a requesting vCPU runs no more guest code until a flush that comes after its request; the first item to run flushes and the rest find nothing pending.PANDA_LOOP_FLUSHis removed.Each patch is amended in place. The ORIGINS rows are unchanged,
base.json'spatchedTreeis re-recorded, and thedocs/vanilla-gates.tomlhashes are refreshed.Cost
Callgrind host instructions on the vCPU thread, guest
getppidloop (scwork), nothing subscribed. Per-function counts are the evidence. Whole-window totals move by a few percent between runs with guest timer ticks.helper_lookup_tb_ptr, per call: beforehelper_lookup_tb_ptr, per call: afterhelper_lookup_tb_ptr, per call: stock QEMU 11.1.2 built PICA jump-cache miss costs about 30 more instructions than before, because the hook check now runs there. Misses are 0.3 to 11 per syscall in these workloads.
Tests
engine/scripts/verify-series.sh 11.1.2 … --record: 101 patches apply with strict context.tests/static/vanilla_inventory.py: PASS.engine/tests/guest/hktest.shon x86_64 and aarch64: all 11 cases PASS on both (count, direct, many, alias, dyn, multi, kern, cond, smp, rp, late).tests/flush_smp.py(new): two vCPUs request a flush while the other's is pending; the second requester must not run its old blocks. Fails on the previous version of 0081, passes on x86_64, aarch64 and riscv64.mem.pyandapi.py(the pandare2 flush tests that cover 0081) on x86_64 and aarch64 Debian 13: pass. Thenix buildof.#pythonandtests/smoke.py x86_64: pass.