From 7c0edbb421425bcabebc8f192bff556d8dd5135d Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 7 May 2026 00:24:28 -0700 Subject: [PATCH] feat(editor): add --bench-migrate-data-tree wall-clock perf benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Times each step of --migrate-data-tree (m2/wmo/blp/dbc) end-to-end and reports wall-clock per step plus the total. Useful for capacity planning ("how long will the full extracted Data tree take?") and regression detection (a recent change shouldn't make M2 conversion 2x slower). Sub-batches dispatched the same way --migrate-data-tree dispatches them, so the timings are exactly what the user will experience running the migration. Both human (table with share %) and --json output modes. Verified: 4-format synthetic tree → all 4 steps timed individually, share percentages sum to 100, total reported in both ms and seconds. M2 + WMO dominate the share even on empty inputs (AssetManager init overhead surfaces here, useful insight). Brings command count to 191. --- tools/editor/main.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 8ccc6746..e3a5d32e 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -518,6 +518,8 @@ static void printUsage(const char* argv0) { std::printf(" Delete proprietary files (.m2/.wmo/.blp/.dbc) that already have an open sidecar\n"); std::printf(" --audit-data-tree \n"); std::printf(" CI gate: exit 1 if any proprietary file lacks an open sidecar (100%% migration check)\n"); + std::printf(" --bench-migrate-data-tree [--json]\n"); + std::printf(" Time each step of --migrate-data-tree (m2/wmo/blp/dbc) and report wall-clock per step\n"); std::printf(" --convert-dbc-json [out.json]\n"); std::printf(" Convert one DBC file to wowee JSON sidecar format\n"); std::printf(" --convert-json-dbc [out.dbc]\n"); @@ -900,6 +902,7 @@ int main(int argc, char* argv[]) { "--validate-whm", "--validate-all", "--validate-project", "--validate-project-open-only", "--audit-project", "--bench-validate-project", "--bench-bake-project", + "--bench-migrate-data-tree", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp", @@ -13557,6 +13560,76 @@ int main(int argc, char* argv[]) { std::printf("\n %d step(s) reported failures (re-run individually for detail)\n", totalFailed); return 1; + } else if (std::strcmp(argv[i], "--bench-migrate-data-tree") == 0 && i + 1 < argc) { + // Time each --migrate-data-tree step end-to-end. Useful + // for capacity planning ("how long will the full extracted + // Data tree take?") and regression detection (a recent + // change shouldn't make M2 conversion 2x slower). + // + // Sub-batches are dispatched the same way --migrate-data- + // tree dispatches them — so the timings here are exactly + // what the user will experience running the migration. + std::string srcDir = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + namespace fs = std::filesystem; + if (!fs::exists(srcDir) || !fs::is_directory(srcDir)) { + std::fprintf(stderr, + "bench-migrate-data-tree: %s is not a directory\n", + srcDir.c_str()); + return 1; + } + std::string self = argv[0]; + struct Step { + const char* name; + const char* flag; + double ms = 0; + int rc = 0; + }; + std::vector steps = { + {"M2 → WOM ", "--convert-m2-batch", 0, 0}, + {"WMO → WOB ", "--convert-wmo-batch", 0, 0}, + {"BLP → PNG ", "--convert-blp-batch", 0, 0}, + {"DBC → JSON", "--convert-dbc-batch", 0, 0}, + }; + double totalMs = 0; + for (auto& s : steps) { + std::string cmd = "\"" + self + "\" " + s.flag + " \"" + srcDir + "\""; + cmd += " >/dev/null 2>&1"; + auto t0 = std::chrono::steady_clock::now(); + s.rc = std::system(cmd.c_str()); + auto t1 = std::chrono::steady_clock::now(); + s.ms = std::chrono::duration(t1 - t0).count(); + totalMs += s.ms; + } + if (jsonOut) { + nlohmann::json j; + j["srcDir"] = srcDir; + j["totalMs"] = totalMs; + nlohmann::json arr = nlohmann::json::array(); + for (const auto& s : steps) { + double share = totalMs > 0 ? 100.0 * s.ms / totalMs : 0.0; + arr.push_back({{"name", s.name}, + {"flag", s.flag}, + {"ms", s.ms}, + {"share", share}, + {"rc", s.rc}}); + } + j["steps"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("bench-migrate-data-tree: %s\n", srcDir.c_str()); + std::printf(" total : %.1f ms (%.2f s)\n", totalMs, totalMs / 1000.0); + std::printf("\n step wall-clock share status\n"); + for (const auto& s : steps) { + double share = totalMs > 0 ? 100.0 * s.ms / totalMs : 0.0; + std::printf(" %-15s %8.1f ms %5.1f%% %s (rc=%d)\n", + s.name, s.ms, share, + s.rc == 0 ? "ok" : "FAIL", s.rc); + } + return 0; } else if (std::strcmp(argv[i], "--info-data-tree") == 0 && i + 1 < argc) { // Non-destructive companion to --migrate-data-tree. Walks // recursively, counts files per format pair