mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
tools/perf: M2-first CPU profiling rig + runner --perf-jitdump
Step 0 of the `neither` cherry-pick funnel: a repeatable, attributable, wallclock-anchored CPU bottleneck baseline for our own ARM64 port, since the RK3562-era numbers are stale and the target has shifted to Snapdragon 865. Built and validated on M2 Max / Asahi first; the same scripts run on SD865 with a new devices/<label>.env and no code change. Test-harness change (no production code path, no shipped env gate): - pcsx2-eerunner --perf-jitdump: emit a Linux perf jitdump under EmuFolders::Cache so `perf inject --jit` resolves EE_/VU0_/VU1_/IOP_/VIF_ block symbols. The enable is driven through the EmuCore/Profiler EnablePerfDump config bool (ApplySettings -> LoadSettings re-applies Perf::SetJitDumpEnabled every apply, so a manual enable would be reset). SetJitDumpDir is set before the first block compiles. Also honors an explicit `--renderer null` under --perf-jitdump for the CPU-only diagnostic. - pcsx2-vurunner --perf-jitdump: same flag for VU-only captures (manual enable; the runner doesn't go through ApplySettings). Tooling (pure stdlib + bash): - tools/perf/bucket_perf.py: parse a `perf report --stdio` dump into a PCSX2 subsystem ranking. Buckets tuned against real M2 R&C UYA / Katamari captures: JIT-by-prefix (EE/VU0/VU1/IOP/VIF) + VU-glue (mVUlookupProg / dispatch envelope) + native VIF + GS (incl. GIF decode + XXH3) + a GPU-driver bucket that quarantines the host Asahi/Vulkan/DRM stack (host-specific, NOT an SD865 proxy) + JIT-other (unsymbolized continuation blocks) + startup/io + a visible `unattributed`. - tools/perf/profile_run.sh: one-command wrapper (precondition gate -> perf record -> inject --jit -> single report dump -> bucket -> median wallclock + median per-bucket share -> summary.md). Device/scene parameterized. - tools/perf/devices/m2max-asahi.env: the working M2 incantation (P-core PMU apple_avalanche_pmu/cycles/, -F 999). - tools/perf/scenes/*.env: R&C UYA + Katamari cinematic/gameplay scenes (assets are copyrighted, not checked in; paths reflect this dev box). Validated on M2: go/no-go #1 (perf+jitdump resolves 277 JIT symbols) and go/no-go #2 (per-bucket shares stable across 3 runs) both green. First finding: on M2 the `vk` profile is dominated by the Asahi GPU stack (GS thread ~57% of samples) — the CPU shape comes from `--renderer null`, where VU (bodies+glue) is the largest emulation cost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f016d603d2
commit
a187fdc0b6
+35
-1
@@ -43,6 +43,7 @@
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/MemorySettingsInterface.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/Perf.h"
|
||||
#include "common/ProgressCallback.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
@@ -100,6 +101,7 @@ static bool s_no_console = false;
|
||||
static bool s_contmem_vu0_interp = false; // --vu0-interp modifier for --contmem
|
||||
static GSRendererType s_renderer = GSRendererType::Null; // --renderer (Null default; vk for Intel/headless)
|
||||
static std::string s_memdump_prefix; // --memdump <prefix>: write <prefix>.{interp,jit}.bin at the last frame
|
||||
static bool s_perf_jitdump = false; // --perf-jitdump: emit Linux perf jitdump for `perf inject --jit` (profiling)
|
||||
|
||||
bool EERunner::InitializeConfig()
|
||||
{
|
||||
@@ -497,6 +499,9 @@ static void PrintCommandLineHelp(const char* progname)
|
||||
std::fprintf(stderr, " --savestate <file>: Savestate to load after Initialize (required).\n");
|
||||
std::fprintf(stderr, " --frames N: Number of frames to run (default 300).\n");
|
||||
std::fprintf(stderr, " --iso <file>: Game ISO/disc to mount (required so the savestate has its disc).\n");
|
||||
std::fprintf(stderr, " --perf-jitdump: Emit a Linux perf jitdump (under EmuFolders::Cache) so `perf inject --jit`\n");
|
||||
std::fprintf(stderr, " resolves EE_/VU0_/VU1_/IOP_/VIF_ JIT block symbols. Profiling only; with --liverun it\n");
|
||||
std::fprintf(stderr, " also honors an explicit --renderer null. Requires a USE_PERF_JITDUMP build.\n");
|
||||
std::fprintf(stderr, " -help: Displays this information and exits.\n");
|
||||
std::fprintf(stderr, " -version: Displays version information and exits.\n");
|
||||
std::fprintf(stderr, "\n");
|
||||
@@ -570,6 +575,15 @@ bool EERunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
|
||||
s_mode = RunMode::LiveRun;
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG("--perf-jitdump"))
|
||||
{
|
||||
// Emit a Linux perf jitdump so `perf inject --jit` can resolve EE_/VU1_/...
|
||||
// JIT block symbols. Profiling only; honors explicit --renderer null (the
|
||||
// liverun null->VK force below is skipped when this is set). Requires a
|
||||
// USE_PERF_JITDUMP build (no-op otherwise).
|
||||
s_perf_jitdump = true;
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG("--disasm"))
|
||||
{
|
||||
// --disasm: load the savestate, then disassemble EE code in
|
||||
@@ -700,7 +714,11 @@ void EERunner::SettingsOverride()
|
||||
// like Null) and MTVU. Null GS is meaningless for it, so force VK if unset.
|
||||
const bool live = (s_mode == RunMode::LiveRun);
|
||||
GSRendererType rend = s_renderer;
|
||||
if (live && rend == GSRendererType::Null)
|
||||
// Liverun normally needs a real GS (Null drops GIF/PATH3), so Null is forced to VK.
|
||||
// When profiling (--perf-jitdump), honor an explicit --renderer null so the
|
||||
// "scalar EE/IOP minus GS-feeding" diagnostic baseline is reachable. vk stays the
|
||||
// representative whole-system profile; null is the secondary diagnostic.
|
||||
if (live && rend == GSRendererType::Null && !s_perf_jitdump)
|
||||
rend = GSRendererType::VK;
|
||||
s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", static_cast<int>(rend));
|
||||
|
||||
@@ -778,6 +796,11 @@ void EERunner::SettingsOverride()
|
||||
s_settings_interface.SetStringValue("SPU2/Output", "Backend", "Null");
|
||||
s_settings_interface.SetStringValue("SPU2/Output", "SyncMode", "Disabled");
|
||||
|
||||
// Profiling: drive the perf jitdump enable through the normal config path so
|
||||
// ApplySettings/LoadSettings (which re-applies Perf::SetJitDumpEnabled from this
|
||||
// bool) keeps it on for the whole run instead of resetting it to the default.
|
||||
s_settings_interface.SetBoolValue("EmuCore/Profiler", "EnablePerfDump", s_perf_jitdump);
|
||||
|
||||
// No frameskip.
|
||||
s_settings_interface.SetBoolValue("EmuCore/GS", "FrameSkipEnable", false);
|
||||
s_settings_interface.SetIntValue("EmuCore/GS", "FramesToDraw", 1);
|
||||
@@ -2889,6 +2912,17 @@ static void CPUThreadMain(VMBootParameters* params, std::atomic<int>* ret)
|
||||
|
||||
if (VMManager::Internal::CPUThreadInitialize())
|
||||
{
|
||||
// Profiling: set the jitdump output dir before any JIT block compiles (the
|
||||
// first compile happens during the first FrameAdvance, well after this). Dir =
|
||||
// EmuFolders::Cache so the 100s-of-MB dump avoids /tmp/tmpfs, matching the
|
||||
// production rationale in common/Perf.cpp. The ENABLE flag is driven through the
|
||||
// normal settings path instead (EmuCore/Profiler EnablePerfDump, set in the
|
||||
// harness config) — ApplySettings() below calls LoadSettings() which re-applies
|
||||
// Perf::SetJitDumpEnabled(EnablePerfDump), so a manual enable here would just get
|
||||
// reset to the config default (false). No-op on non-jitdump builds.
|
||||
if (s_perf_jitdump)
|
||||
Perf::SetJitDumpDir(EmuFolders::Cache);
|
||||
|
||||
// apply new settings (e.g. pick up renderer change)
|
||||
VMManager::ApplySettings();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user