From d3ccda6d6eb92abb1d8f3f29e8526a02143e7e6a Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Tue, 14 May 2024 23:14:48 -0400 Subject: [PATCH] goalc: add a performance report feature --- .gitignore | 1 + .../config/jak3/ntsc_v1/type_casts.jsonc | 12 ++--- goal_src/goal-lib.gc | 8 +++- .../compiler/compilation/CompilerControl.cpp | 10 ++++- goalc/make/CompilerReport.h | 41 +++++++++++++++++ goalc/make/MakeSystem.cpp | 45 ++++++++++++++++--- goalc/make/MakeSystem.h | 2 +- 7 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 goalc/make/CompilerReport.h diff --git a/.gitignore b/.gitignore index 3884a00c7..e2d7c668d 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ __pycache__/ /TODO.md unifont-15.0.03.ttf *.diff +goalc-report.html \ No newline at end of file diff --git a/decompiler/config/jak3/ntsc_v1/type_casts.jsonc b/decompiler/config/jak3/ntsc_v1/type_casts.jsonc index 749c79310..d50a5dccf 100644 --- a/decompiler/config/jak3/ntsc_v1/type_casts.jsonc +++ b/decompiler/config/jak3/ntsc_v1/type_casts.jsonc @@ -10846,9 +10846,7 @@ ["_stack_", 76, "float"], ["_stack_", 100, "float"] ], - "real-wang-texture-anim-func": [ - [[3, 31], "v1", "mood-context"] - ], + "real-wang-texture-anim-func": [[[3, 31], "v1", "mood-context"]], "(method 24 sky-work)": [ [256, "s4", "(pointer int32)"], [261, "s4", "(pointer int32)"] @@ -10865,12 +10863,8 @@ [67, "s5", "(pointer int32)"], [72, "s5", "(pointer int32)"] ], - "(method 38 sky-work)": [ - [[83, 179], "v1", "(inline-array qword)"] - ], - "(method 14 sky-work)": [ - [[80, 256], "s4", "sky-work"] - ], + "(method 38 sky-work)": [[[83, 179], "v1", "(inline-array qword)"]], + "(method 14 sky-work)": [[[80, 256], "s4", "sky-work"]], "(method 13 sky-work)": [ [65, "v1", "(pointer uint128)"], [[70, 77], "a0", "vector4w"], diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 36da959ae..440c942e7 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -997,8 +997,8 @@ `(make ,(string-append "$OUT/iso/" file ".DGO")) ) -(defmacro make-group (name &key (verbose #f) &key (force #f)) - `(make ,(string-append "GROUP:" name) :verbose ,verbose :force ,force) +(defmacro make-group (name &key (verbose #f) &key (force #f) &key (report #f)) + `(make ,(string-append "GROUP:" name) :verbose ,verbose :force ,force :report ,report) ) (defmacro rl () @@ -1014,6 +1014,10 @@ `(make-group "iso") ) +(defmacro mi-report () + "Make ISO with Report" + `(make-group "iso" :report #t)) + (defmacro mkr () "Make kernel" `(make-group "kernel") diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 3e61a12bb..7fcdd33b8 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -824,7 +824,8 @@ Val* Compiler::compile_make(const goos::Object& form, const goos::Object& rest, auto args = get_va(form, rest); va_check(form, args, {goos::ObjectType::STRING}, {{"force", {false, {goos::ObjectType::SYMBOL}}}, - {"verbose", {false, {goos::ObjectType::SYMBOL}}}}); + {"verbose", {false, {goos::ObjectType::SYMBOL}}}, + {"report", {false, {goos::ObjectType::SYMBOL}}}}); bool force = false; if (args.has_named("force")) { force = get_true_or_false(form, args.get_named("force")); @@ -835,7 +836,12 @@ Val* Compiler::compile_make(const goos::Object& form, const goos::Object& rest, verbose = get_true_or_false(form, args.get_named("verbose")); } - m_make.make(args.unnamed.at(0).as_string()->data, force, verbose); + bool report = false; + if (args.has_named("report")) { + report = get_true_or_false(form, args.get_named("report")); + } + + m_make.make(args.unnamed.at(0).as_string()->data, force, verbose, report); return get_none(); } diff --git a/goalc/make/CompilerReport.h b/goalc/make/CompilerReport.h new file mode 100644 index 000000000..29986d5cc --- /dev/null +++ b/goalc/make/CompilerReport.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +const std::string compiler_report_base = + "OpenGOALc " + "Report</" + "title><style>html{font-size:10pt;font-family:sans-serif}table{width:100%;border-collapse:" + "collapse;margin:20px 0;box-shadow:0 2px 3px rgba(0,0,0,.1)}thead " + "th{background-color:#4caf50;color:#fff;text-align:left;padding:12px " + "15px;font-weight:700}tbody tr{border-bottom:1px solid #ddd}tbody td{padding:12px 15px}tbody " + "tr:nth-child(even){background-color:#f2f2f2}tbody " + "tr:hover{background-color:#f1f1f1}table{margin:20px 0}</style><canvas height=75vh " + "id=chart></canvas><br>Compare Against: <select id=compare></select> Accepted Margin of Error " + "%: <input id=margin type=number value=15>File Regex: <input id=regex><button " + "onclick=renderData()>Apply</button><table id=table><thead><tr " + "id=table-head-row><th>File<tbody id=table-body></table><script " + "src=https://cdn.jsdelivr.net/npm/chart.js></script><script>\n// DATA BEGINS\nconst tests = " + "[]\n// DATA ENDS\nlet testOrder=[],tableData={};for(const test of " + "tests)for(const[fileName,fileTime]of(testOrder.push(test.name),document.getElementById(" + "\"compare\").innerHTML+=`<option " + "value=\"${test.name}\">${test.name}</" + "option>`,Object.entries(test.files)))Object.keys(tableData).includes(fileName)||(tableData[" + "fileName]={}),tableData[fileName][test.name]=fileTime;const " + "tableHeadRow=document.getElementById(\"table-head-row\");let tableHeadContents=\"\";for(const " + "testName of testOrder)tableHeadContents+=`<th>${testName}</th>`;function renderData(){let " + "e=parseInt(document.getElementById(\"margin\").value)/" + "100,t=document.getElementById(\"regex\").value;t=\"\"!==t?RegExp(t):void 0;let " + "a=document.getElementById(\"compare\").value;\"\"!==a&&a||(a=testOrder[0]);let " + "l=document.getElementById(\"table-body\");for(let[n,d]of(l.innerHTML=\"\",Object.entries(" + "tableData)))if(void 0===t||t.test(n)){var o=`<tr><td>${n}</td>`;for(let r of " + "testOrder)Object.keys(d).includes(r)?a!==r&&Object.keys(d).includes(a)?d[r]>d[a]*(1+e)?o+=`<" + "td style=\"background-color: #faa;\">${d[r]}</td>`:d[r]<d[a]*(1-e)?o+=`<td " + "style=\"background-color: " + "#c7ffc7;\">${d[r]}</td>`:o+=`<td>${d[r]}</td>`:o+=`<td>${d[r]}</td>`:o+=\"<td>--</" + "td>\";l.innerHTML+=o+\"</tr>\"}}tableHeadRow.innerHTML+=tableHeadContents,renderData();const " + "ctx=document.getElementById(\"chart\");new " + "Chart(ctx,{type:\"bar\",data:{labels:tests.map(e=>e.name),datasets:[{label:\"Total " + "Time\",data:tests.map(e=>e.total),borderWidth:1}]},options:{scales:{y:{beginAtZero:!0,type:" + "\"logarithmic\",title:{display:!0,text:\"Seconds\"}}}}});</script>"; diff --git a/goalc/make/MakeSystem.cpp b/goalc/make/MakeSystem.cpp index dd6235534..6f54e4d4c 100644 --- a/goalc/make/MakeSystem.cpp +++ b/goalc/make/MakeSystem.cpp @@ -6,6 +6,7 @@ #include "common/util/Timer.h" #include "common/util/string_util.h" +#include "goalc/make/CompilerReport.h" #include "goalc/make/Tools.h" #include "fmt/color.h" @@ -438,7 +439,7 @@ void print_input(const std::vector<std::string>& in, char end) { } } // namespace -bool MakeSystem::make(const std::string& target_in, bool force, bool verbose) { +bool MakeSystem::make(const std::string& target_in, bool force, bool verbose, bool gen_report) { std::string target = m_path_map.apply_remaps(target_in); auto deps = get_dependencies(target); // lg::print("All deps:\n"); @@ -454,6 +455,22 @@ bool MakeSystem::make(const std::string& target_in, bool force, bool verbose) { // lg::print("{}\n", dep); // } + fs::path report_path; + std::string report_output; + std::string report_contents; + if (gen_report) { + report_path = file_util::get_jak_project_dir() / "goalc-report.html"; + lg::print("Will save compiler report to - {}", report_path.string()); + // Check if a report is already there, if it is, we'll append to it instead of overwriting it + if (file_util::file_exists(report_path.string())) { + report_output = file_util::read_text_file(report_path); + } else { + report_output = compiler_report_base; + } + report_contents += fmt::format("tests.push({{'name': \"Test - {}\",'files': {{", + str_util::current_isotimestamp()); + } + Timer make_timer; lg::print("Building {} targets...\n", deps.size()); int i = 0; @@ -483,25 +500,39 @@ bool MakeSystem::make(const std::string& target_in, bool force, bool verbose) { return false; } + const auto seconds = step_timer.getSeconds(); if (verbose) { - if (step_timer.getSeconds() > 0.05) { - lg::print(fg(fmt::color::yellow), " {:.3f}\n", step_timer.getSeconds()); + if (seconds > 0.05) { + lg::print(fg(fmt::color::yellow), " {:.3f}\n", seconds); } else { - lg::print(" {:.3f}\n", step_timer.getSeconds()); + lg::print(" {:.3f}\n", seconds); } } else { - if (step_timer.getSeconds() > 0.05) { + if (seconds > 0.05) { lg::print("[{:3d}%] [{:8s}] ", percent, tool->name()); - lg::print(fg(fmt::color::yellow), "{:.3f} ", step_timer.getSeconds()); + lg::print(fg(fmt::color::yellow), "{:.3f} ", seconds); print_input(rule->input, '\n'); } else { - lg::print("[{:3d}%] [{:8s}] {:.3f} ", percent, tool->name(), step_timer.getSeconds()); + lg::print("[{:3d}%] [{:8s}] {:.3f} ", percent, tool->name(), seconds); print_input(rule->input, '\n'); } } + + if (gen_report) { + report_contents += + fmt::format("\"{}\": {}{}", str_util::split_string(rule->input.at(0), "/").back(), + seconds, i == deps.size() ? "" : ","); + } } lg::print("\nSuccessfully built all {} targets in {:.3f}s\n", deps.size(), make_timer.getSeconds()); + if (gen_report) { + report_contents += fmt::format("}}, 'total': {}}});", make_timer.getSeconds()); + str_util::replace(report_output, "// DATA ENDS\n", + fmt::format("{}\n// DATA ENDS\n", report_contents)); + file_util::write_text_file(report_path, report_output); + lg::print("Saved report to: {}\n", report_path.string()); + } return true; } diff --git a/goalc/make/MakeSystem.h b/goalc/make/MakeSystem.h index 99f168db5..413a07259 100644 --- a/goalc/make/MakeSystem.h +++ b/goalc/make/MakeSystem.h @@ -57,7 +57,7 @@ class MakeSystem { std::vector<std::string> get_dependencies(const std::string& target) const; std::vector<std::string> filter_dependencies(const std::vector<std::string>& all_deps); - bool make(const std::string& target, bool force, bool verbose); + bool make(const std::string& target, bool force, bool verbose, bool gen_report); void add_tool(std::shared_ptr<Tool> tool); void set_constant(const std::string& name, const std::string& value);