mirror of
https://github.com/godotengine/godot-benchmarks.git
synced 2025-12-31 09:49:13 +03:00
Add graphs to the website (#63)
Co-authored-by: Hugo Locurcio <hugo.locurcio@hugo.pro>
This commit is contained in:
@@ -1,60 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import random
|
||||
|
||||
NUM_EXPRESSIONS = 20
|
||||
NUM_VARS = 3000 # Should be kept in sync with NUM_VARS in expression.gd
|
||||
|
||||
|
||||
def _var_names():
|
||||
rv = []
|
||||
for i in range(NUM_VARS):
|
||||
rv.append("x" + str(i))
|
||||
return rv
|
||||
rv = []
|
||||
for i in range(NUM_VARS):
|
||||
rv.append("x" + str(i))
|
||||
return rv
|
||||
|
||||
|
||||
def _var_values():
|
||||
rv = []
|
||||
for i in range (NUM_VARS):
|
||||
rv.append((i+1)*10)
|
||||
return rv
|
||||
rv = []
|
||||
for i in range(NUM_VARS):
|
||||
rv.append((i + 1) * 10)
|
||||
return rv
|
||||
|
||||
|
||||
def _combine(nodes, ia, ib, op):
|
||||
na = nodes[ia]
|
||||
nb = nodes[ib]
|
||||
del nodes[ib]
|
||||
del nodes[ia]
|
||||
nodes.append("(" + str(na) + " " + op + " " + str(nb) + ")")
|
||||
na = nodes[ia]
|
||||
nb = nodes[ib]
|
||||
del nodes[ib]
|
||||
del nodes[ia]
|
||||
nodes.append("(" + str(na) + " " + op + " " + str(nb) + ")")
|
||||
|
||||
|
||||
def _generate_string():
|
||||
nodes = []
|
||||
operators = ["+", "-", "*", "/"]
|
||||
nodes = []
|
||||
operators = ["+", "-", "*", "/"]
|
||||
|
||||
nodes += _var_names()
|
||||
nodes += _var_names()
|
||||
|
||||
while len(nodes) > 1:
|
||||
ia = random.randrange(0, len(nodes))
|
||||
ib = random.randrange(0, len(nodes))
|
||||
io = random.randrange(0, len(operators))
|
||||
op = operators[io]
|
||||
while len(nodes) > 1:
|
||||
ia = random.randrange(0, len(nodes))
|
||||
ib = random.randrange(0, len(nodes))
|
||||
io = random.randrange(0, len(operators))
|
||||
op = operators[io]
|
||||
|
||||
if ia == ib:
|
||||
ib = ia+1
|
||||
if ib == len(nodes):
|
||||
ib = 0
|
||||
if ia == ib:
|
||||
ib = ia + 1
|
||||
if ib == len(nodes):
|
||||
ib = 0
|
||||
|
||||
if ia < ib:
|
||||
_combine(nodes, ia, ib, op)
|
||||
elif ib < ia:
|
||||
_combine(nodes, ib, ia, op)
|
||||
if ia < ib:
|
||||
_combine(nodes, ia, ib, op)
|
||||
elif ib < ia:
|
||||
_combine(nodes, ib, ia, op)
|
||||
|
||||
return nodes[0]
|
||||
|
||||
return nodes[0]
|
||||
|
||||
def _generate_strings():
|
||||
random.seed(234)
|
||||
rv = []
|
||||
for i in range(NUM_EXPRESSIONS):
|
||||
rv.append(_generate_string())
|
||||
return rv
|
||||
random.seed(234)
|
||||
rv = []
|
||||
for i in range(NUM_EXPRESSIONS):
|
||||
rv.append(_generate_string())
|
||||
return rv
|
||||
|
||||
|
||||
strings = _generate_strings()
|
||||
print("const EXPRESSIONS = ", json.dumps(strings, indent=4).replace(' ', '\t'))
|
||||
print("const EXPRESSIONS = ", json.dumps(strings, indent=4).replace(" ", "\t"))
|
||||
|
||||
12
web/.gitignore
vendored
12
web/.gitignore
vendored
@@ -4,7 +4,11 @@ public/
|
||||
# Temporary lock file while building
|
||||
.hugo_build.lock
|
||||
|
||||
# Result JSON files
|
||||
# (should be committed to https://github.com/godotengine/godot-benchmarks-results instead)
|
||||
content/*.md
|
||||
!_content/_index.md
|
||||
# Generated files
|
||||
content/benchmark/*.md
|
||||
content/graph/*.md
|
||||
data/data.json
|
||||
|
||||
# Untracked source files
|
||||
src-data/benchmarks/*.md
|
||||
src-data/benchmarks/*.json
|
||||
|
||||
102
web/generate-content.py
Executable file
102
web/generate-content.py
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Usage:
|
||||
# generate-content.py [benchmarks-folder]
|
||||
#
|
||||
# This script generates the content and data files for hugo.
|
||||
# This should be ran before trying to build the site.
|
||||
#
|
||||
# It takes as input two files, a "graph.json" in the ./src-data folder,
|
||||
# and the results of our godot benchmarks (produced by ../run-benchmarks.sh)
|
||||
# By default, benchmarks (.json or .md, but all should be JSON inside ¯\_(ツ)_/¯)
|
||||
# are taken from the "./src-data/benchmarks". But you can specify an optional
|
||||
# folder otherwise as argument.
|
||||
|
||||
import json
|
||||
import sys
|
||||
from os import listdir
|
||||
from os.path import isdir, isfile, join
|
||||
|
||||
# Source data paths.
|
||||
graphs_path = "./src-data/graphs.json"
|
||||
if len(sys.argv) == 1:
|
||||
benchmarks_path = "./src-data/benchmarks"
|
||||
elif len(sys.argv) == 2:
|
||||
benchmarks_path = sys.argv[1]
|
||||
if not isdir(benchmarks_path):
|
||||
raise ValueError(benchmarks_path + " is not a valid folder")
|
||||
else:
|
||||
raise ValueError("Invalid number of arguments")
|
||||
|
||||
|
||||
# Bnase data.json dictionary.
|
||||
data_output_json = {
|
||||
"benchmarks": [],
|
||||
"graphs": [],
|
||||
}
|
||||
|
||||
### BENCHMARKS ###
|
||||
|
||||
# Fetch the list of benchmark files
|
||||
benchmark_input_filename_test = lambda f: (f.endswith(".json") or f.endswith(".md"))
|
||||
benchmarks_files = [
|
||||
f for f in listdir(benchmarks_path) if (isfile(join(benchmarks_path, f)) and benchmark_input_filename_test(f))
|
||||
]
|
||||
|
||||
# Add the list of benchmarks.
|
||||
for f in benchmarks_files:
|
||||
json_file = open(join(benchmarks_path, f))
|
||||
|
||||
# Extract data from filename.
|
||||
key = f.removesuffix(".json")
|
||||
date = key.split("_")[0]
|
||||
commit = key.split("_")[1]
|
||||
|
||||
# Load and modify the benchmark file.
|
||||
output_dict = json.load(json_file)
|
||||
output_dict["date"] = date
|
||||
output_dict["commit"] = commit
|
||||
|
||||
# Merge category and name into a single "path" field.
|
||||
output_benchmark_list = []
|
||||
for benchmark in output_dict["benchmarks"]:
|
||||
output_benchmark_list.append(
|
||||
{
|
||||
"path": [el.strip() for el in benchmark["category"].split(">")] + [benchmark["name"]],
|
||||
"results": benchmark["results"],
|
||||
}
|
||||
)
|
||||
output_dict["benchmarks"] = output_benchmark_list
|
||||
|
||||
# Add it to the list.
|
||||
data_output_json["benchmarks"].append(output_dict)
|
||||
json_file.close()
|
||||
|
||||
### GRAPHS ###
|
||||
|
||||
# Add the graphs.
|
||||
json_file = open(graphs_path)
|
||||
data_output_json["graphs"] = json.load(json_file)
|
||||
json_file.close()
|
||||
|
||||
### DUMPING data.json ###
|
||||
|
||||
# Create a big json with all of the data.
|
||||
data_filename = "./data/data.json"
|
||||
data_file = open(data_filename, "w")
|
||||
json.dump(data_output_json, data_file, indent=4)
|
||||
data_file.close()
|
||||
|
||||
### CREATE .md FILES (for the pages) ###
|
||||
|
||||
# Create a .md file for each benchmark.
|
||||
benchmarks_content_path = "./content/benchmark"
|
||||
for benchmark in data_output_json["benchmarks"]:
|
||||
filename = benchmark["date"] + "_" + benchmark["commit"] + ".md"
|
||||
open(join(benchmarks_content_path, filename), "a").close()
|
||||
|
||||
# Create a .md file for each graph.
|
||||
graphs_content_path = "./content/graph"
|
||||
for graph in data_output_json["graphs"]:
|
||||
filename = graph["id"] + ".md"
|
||||
open(join(graphs_content_path, filename), "a").close()
|
||||
@@ -30,6 +30,9 @@
|
||||
window.location.href = window.location.href.replace('://godotengine.github.io/godot-benchmarks-results/','://benchmarks.godotengine.org/');
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const Database = {{ .Site.Data.data }}
|
||||
</script>
|
||||
{{ block "javascript" . }}{{end}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $date := index (split (path.BaseName .Permalink) "_") 0 }}
|
||||
{{ $commit := index (split (path.BaseName .Permalink) "_") 1 }}
|
||||
|
||||
{{ $benchmark := where .Site.Data.data.benchmarks "commit" "eq" $commit }}
|
||||
{{ $benchmark := where $benchmark "date" "eq" $date }}
|
||||
{{ $benchmark := index $benchmark 0 }}
|
||||
|
||||
<h1>{{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||
<a href="https://github.com/godotengine/godot/commit/{{ .Params.engine.version_hash }}"><code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a></h1>
|
||||
<a href="https://github.com/godotengine/godot/commit/{{ $benchmark.engine.version_hash }}"><code>{{ slicestr $benchmark.engine.version_hash 0 9 }}</code></a>
|
||||
</h1>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 10px;">
|
||||
{{/* Order is inverted for this site. */}}
|
||||
<div>
|
||||
{{with .Site.RegularPages.Next . }}
|
||||
<a href="{{ .RelPermalink }}">« Previous: {{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a>
|
||||
<code>{{ slicestr $benchmark.engine.version_hash 0 9 }}</code>
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
<div style="text-align: right">
|
||||
{{with .Site.RegularPages.Prev . }}
|
||||
<a href="{{ .RelPermalink }}">Next: {{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code> »</a>
|
||||
<code>{{ slicestr $benchmark.engine.version_hash 0 9 }}</code> »
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,36 +61,36 @@
|
||||
<tr>
|
||||
<td><abbr title="Using GCC compiler with all caches cleared">Time to build</abbr></td>
|
||||
<td>
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.build_time.debug 0.001 | lang.FormatNumber 0 }} seconds<br></span>
|
||||
<sub>Release</sub> {{ mul .Params.build_time.release 0.001 | lang.FormatNumber 0 }} seconds
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul $benchmark.build_time.debug 0.001 | lang.FormatNumber 0 }} seconds<br></span>
|
||||
<sub>Release</sub> {{ mul $benchmark.build_time.release 0.001 | lang.FormatNumber 0 }} seconds
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><abbr title="Using GCC compiler with all caches cleared">Build peak memory usage</abbr></td>
|
||||
<td>
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.build_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||
<sub>Release</sub> {{ mul .Params.build_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul $benchmark.build_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||
<sub>Release</sub> {{ mul $benchmark.build_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><abbr title="Measured on an empty project">Startup + shutdown time</abbr></td>
|
||||
<td>
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ .Params.empty_project_startup_shutdown_time.debug | lang.FormatNumber 0 }} ms<br></span>
|
||||
<sub>Release</sub> {{ .Params.empty_project_startup_shutdown_time.release | lang.FormatNumber 0 }} ms
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ $benchmark.empty_project_startup_shutdown_time.debug | lang.FormatNumber 0 }} ms<br></span>
|
||||
<sub>Release</sub> {{ $benchmark.empty_project_startup_shutdown_time.release | lang.FormatNumber 0 }} ms
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><abbr title="Measured on an empty project">Startup + shutdown peak memory usage</abbr></td>
|
||||
<td>
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.empty_project_startup_shutdown_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||
<sub>Release</sub> {{ mul .Params.empty_project_startup_shutdown_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul $benchmark.empty_project_startup_shutdown_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||
<sub>Release</sub> {{ mul $benchmark.empty_project_startup_shutdown_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><abbr title="Measured after stripping debug symbols">Binary size</abbr></td>
|
||||
<td>
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.binary_size.debug 0.001 | lang.FormatNumber 0 }} KB<br></span>
|
||||
<sub>Release</sub> {{ mul .Params.binary_size.release 0.001 | lang.FormatNumber 0 }} KB
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul $benchmark.binary_size.debug 0.001 | lang.FormatNumber 0 }} KB<br></span>
|
||||
<sub>Release</sub> {{ mul $benchmark.binary_size.release 0.001 | lang.FormatNumber 0 }} KB
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -100,10 +111,16 @@
|
||||
<tbody>
|
||||
{{/* Check CPU debug data only, but also get data from release CPU runs. */}}
|
||||
{{/* These runs are expected to have the same number of results available. */}}
|
||||
{{ range .Params.benchmarks }}
|
||||
{{ range $benchmark.benchmarks }}
|
||||
{{ if gt .results.cpu_debug.time 0 }}
|
||||
<tr>
|
||||
<td><sub style="opacity: 0.65">{{ .category }}</sub><br><strong>{{ .name }}</strong></td>
|
||||
<td>
|
||||
<sub style="opacity: 0.65">
|
||||
{{ delimit (first (sub (len .path) 1) .path) " > "}}
|
||||
</sub>
|
||||
<br>
|
||||
<strong>{{ index (last 1 .path) 0 }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ if gt .results.cpu_debug.idle 0 }}
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ .results.cpu_debug.idle }} <sub>mspf</sub><br></span>
|
||||
@@ -113,7 +130,7 @@
|
||||
<td>
|
||||
{{ if gt .results.cpu_debug.physics 0 }}
|
||||
<span style="opacity: 0.65"><sub>Debug</sub> {{ .results.cpu_debug.physics }} <sub>mspf</sub><br></span>
|
||||
<sub>Release</sub> {{ .results.cpu_debug.physics }} <sub>mspf</sub>
|
||||
<sub>Release</sub> {{ .results.cpu_release.physics }} <sub>mspf</sub>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
@@ -142,10 +159,16 @@
|
||||
<tbody>
|
||||
{{/* Check GPU AMD data only, but also get data from Intel and NVIDIA GPU runs. */}}
|
||||
{{/* These runs are expected to have the same number of results available. */}}
|
||||
{{ range .Params.benchmarks }}
|
||||
{{ range $benchmark.benchmarks }}
|
||||
{{ if gt .results.amd.render_cpu 0 }}
|
||||
<tr>
|
||||
<td><sub style="opacity: 0.65">{{ .category }}</sub><br><strong>{{ .name }}</strong></td>
|
||||
<td>
|
||||
<sub style="opacity: 0.65">
|
||||
{{ delimit (first (sub (len .path) 1) .path) " > "}}
|
||||
</sub>
|
||||
<br>
|
||||
<strong>{{ index (last 1 .path) 0 }}</strong></td>
|
||||
</td>
|
||||
<td>
|
||||
{{ if gt .results.amd.render_cpu 0 }}
|
||||
<!-- <span title="Intel HD Graphics">🔵</span> {{ .results.intel.render_cpu }} <sub>mspf</sub><br> -->
|
||||
29
web/layouts/graph/single.html
Normal file
29
web/layouts/graph/single.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $graphID := path.BaseName .Permalink }}
|
||||
{{ $graph := index (where .Site.Data.data.graphs "id" $graphID) 0 }}
|
||||
|
||||
<h1>
|
||||
{{ $graph.title }} (lower is better)
|
||||
</h1>
|
||||
|
||||
<div style="display: flex; flex-direction: column;">
|
||||
<input style="margin-right: 0" type="text" id="filter" name="filter" placeholder="Filter..." oninput="updateChart()"/>
|
||||
<div id="chart" style="min-height: 365px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{end}}
|
||||
|
||||
{{ define "javascript" }}
|
||||
|
||||
<script language="javascript" type="text/javascript" src="{{ "/graphs.js" | urlize | relURL }}"></script>
|
||||
<script>
|
||||
function updateChart() {
|
||||
const filterInput = document.getElementById("filter");
|
||||
displayGraph("#chart", {{ path.BaseName .Permalink }}, "full", filterInput.value );
|
||||
};
|
||||
updateChart()
|
||||
</script>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,6 +1,9 @@
|
||||
{{ define "main" }}
|
||||
<main aria-role="main">
|
||||
|
||||
{{ $benchmarks := .Site.Data.data.benchmarks}}
|
||||
{{ $graphs := .Site.Data.data.graphs}}
|
||||
|
||||
<main aria-role="main">
|
||||
<p>
|
||||
This page tracks <a href="https://godotengine.org/">Godot Engine</a> performance running on a
|
||||
<a href="https://github.com/godotengine/godot-benchmarks">benchmark suite</a>.
|
||||
@@ -8,14 +11,32 @@
|
||||
regressions over time.
|
||||
</p>
|
||||
|
||||
<h2>Graphs</h2>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 1em;">
|
||||
<div style="flex-grow: 3;">
|
||||
Normalized (percentage of the average time), lower is better.
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 50% 50%; gap: 1em;">
|
||||
{{ range $graphs }}
|
||||
<div style="display: flex; flex-direction: column; gap: 1em;">
|
||||
<div style="align-self: center;">
|
||||
<a href="/graph/{{ .id }}">{{ .title }}</a>
|
||||
</div>
|
||||
<div id="{{ .id }}">
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<h2>Latest benchmark runs</h2>
|
||||
<ul>
|
||||
{{ range first 100 .Pages.Reverse }}
|
||||
<li><a href="{{ .Permalink }}">{{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a></li>
|
||||
{{ range sort $benchmarks ".date" "asc" | first 100}}
|
||||
<li>
|
||||
<a href="/benchmark/{{ .date }}_{{ .commit }}">{{ .date }}<code>{{ slicestr .engine.version_hash 0 9 }}</code></a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Benchmarking machine</h2>
|
||||
@@ -73,6 +94,15 @@
|
||||
{{ end }}
|
||||
|
||||
{{ define "javascript" }}
|
||||
<script language="javascript" type="text/javascript" src="{{ "/graphs.js" | urlize | relURL }}"></script>
|
||||
<script>
|
||||
// Update all graphs.
|
||||
function updateGraphs() {
|
||||
let selectTag = document.querySelector('#metric');
|
||||
Database.graphs.forEach((g) => {
|
||||
displayGraph(`#${g.id}`, g.id, "compact")
|
||||
})
|
||||
}
|
||||
updateGraphs()
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
77
web/src-data/graphs.json
Normal file
77
web/src-data/graphs.json
Normal file
@@ -0,0 +1,77 @@
|
||||
[
|
||||
{
|
||||
"id": "core-callables",
|
||||
"title": "Callables",
|
||||
"benchmark-path-prefix": "Core/Callable"
|
||||
},
|
||||
{
|
||||
"id": "core-crypto",
|
||||
"title": "Crypto",
|
||||
"benchmark-path-prefix": "Core/Crypto"
|
||||
},
|
||||
{
|
||||
"id": "core-rng",
|
||||
"title": "Random Number Generator",
|
||||
"benchmark-path-prefix": "Core/Random Number Generator"
|
||||
},
|
||||
{
|
||||
"id": "core-signal",
|
||||
"title": "Signals",
|
||||
"benchmark-path-prefix": "Core/Signal"
|
||||
},
|
||||
{
|
||||
"id": "gdscript-allocations",
|
||||
"title": "GDscript allocations",
|
||||
"benchmark-path-prefix": "Gdscript/Alloc"
|
||||
},
|
||||
{
|
||||
"id": "gdscript-arrays",
|
||||
"title": "GDscript arrays",
|
||||
"benchmark-path-prefix": "Gdscript/Array"
|
||||
},
|
||||
{
|
||||
"id": "gdscript-string-checksum",
|
||||
"title": "GDscript String Checksums",
|
||||
"benchmark-path-prefix": "Gdscript/String Checksum"
|
||||
},
|
||||
{
|
||||
"id": "gdscript-string-format",
|
||||
"title": "GDscript String Format",
|
||||
"benchmark-path-prefix": "Gdscript/String Format"
|
||||
},
|
||||
{
|
||||
"id": "gdscript-string-manipulations",
|
||||
"title": "GDscript String Manipulations",
|
||||
"benchmark-path-prefix": "Gdscript/String Manipulation"
|
||||
},
|
||||
{
|
||||
"id": "physics",
|
||||
"title": "Rigid Body 3D",
|
||||
"benchmark-path-prefix": "Physics/Rigid Body 3d"
|
||||
},
|
||||
{
|
||||
"id": "rendering-culling",
|
||||
"title": "Culling",
|
||||
"benchmark-path-prefix": "Rendering/Culling"
|
||||
},
|
||||
{
|
||||
"id": "rendering-hlod",
|
||||
"title": "Hlod",
|
||||
"benchmark-path-prefix": "Rendering/Hlod"
|
||||
},
|
||||
{
|
||||
"id": "rendering-labels",
|
||||
"title": "Rendering labels",
|
||||
"benchmark-path-prefix": "Rendering/Hlod"
|
||||
},
|
||||
{
|
||||
"id": "rendering-light-and-meches",
|
||||
"title": "Lights and Meshes",
|
||||
"benchmark-path-prefix": "Rendering/Lights And Meshes"
|
||||
},
|
||||
{
|
||||
"id": "rendering-polygon-sprite-2d",
|
||||
"title": "Polygon Sprite 2d",
|
||||
"benchmark-path-prefix": "Rendering/Polygon Sprite 2d"
|
||||
}
|
||||
]
|
||||
209
web/static/graphs.js
Normal file
209
web/static/graphs.js
Normal file
@@ -0,0 +1,209 @@
|
||||
function getAllowedMetrics() {
|
||||
const allowedMetrics = new Set();
|
||||
Database.benchmarks.forEach((benchmark) => {
|
||||
benchmark.benchmarks.forEach((instance) => {
|
||||
Object.entries(instance.results).forEach(([key, value]) => {
|
||||
allowedMetrics.add(key);
|
||||
});
|
||||
});
|
||||
});
|
||||
return allowedMetrics;
|
||||
}
|
||||
|
||||
function displayGraph(targetDivID, graphID, type = "full", filter = "") {
|
||||
if (!["full", "compact"].includes(type)) {
|
||||
throw Error("Unknown chart type");
|
||||
}
|
||||
|
||||
// Include benchmark data JSON to generate graphs.
|
||||
const allBenchmarks = Database.benchmarks.sort(
|
||||
(a, b) => `${a.date}.${a.commit}` > `${b.date}.${b.commit}`,
|
||||
);
|
||||
const graph = Database.graphs.find((g) => g.id == graphID);
|
||||
if (!graph) {
|
||||
throw new Error("Invalid graph ID");
|
||||
}
|
||||
// Group by series.
|
||||
const xAxis = [];
|
||||
const series = new Map();
|
||||
const processResult = (path, data, process) => {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (typeof value === "object") {
|
||||
processResult(path + "/" + key, value, process);
|
||||
} else {
|
||||
// Number
|
||||
process(path + "/" + key, value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Get list all series and fill it in.
|
||||
allBenchmarks.forEach((benchmark, count) => {
|
||||
// Process a day/commit
|
||||
xAxis.push(benchmark.date + "." + benchmark.commit);
|
||||
|
||||
// Get all series.
|
||||
benchmark.benchmarks.forEach((instance) => {
|
||||
let instanceKey = instance.path.join("/");
|
||||
if (!instanceKey.startsWith(graph["benchmark-path-prefix"])) {
|
||||
return;
|
||||
}
|
||||
instanceKey = instanceKey.slice(
|
||||
graph["benchmark-path-prefix"].length + 1,
|
||||
);
|
||||
|
||||
processResult(instanceKey, instance.results, (path, value) => {
|
||||
// Filter out paths that do not fit the filter
|
||||
if (filter && !path.includes(filter)) {
|
||||
return;
|
||||
}
|
||||
if (!series.has(path)) {
|
||||
series.set(path, Array(count).fill(null));
|
||||
}
|
||||
series.get(path).push(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let customColor = undefined;
|
||||
|
||||
if (type === "compact") {
|
||||
// Kind of "normalize" the series, dividing by the average.
|
||||
series.forEach((serie, key) => {
|
||||
let count = 0;
|
||||
let mean = 0.0;
|
||||
serie.forEach((el) => {
|
||||
if (el != null) {
|
||||
mean += el;
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
mean = mean / count;
|
||||
|
||||
//const std = Math.sqrt(input.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n)
|
||||
series.set(
|
||||
key,
|
||||
serie.map((v) => {
|
||||
if (v != null) {
|
||||
return v / mean; // Devide by the mean.
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
);
|
||||
});
|
||||
// Combine all into a single, averaged serie.
|
||||
const outputSerie = [];
|
||||
for (let i = 0; i < allBenchmarks.length; i++) {
|
||||
let count = 0;
|
||||
let sum = 0;
|
||||
series.forEach((serie, key) => {
|
||||
if (serie[i] != null) {
|
||||
count += 1;
|
||||
sum += serie[i];
|
||||
}
|
||||
});
|
||||
let point = null;
|
||||
if (count >= 1) {
|
||||
point = Math.round((sum * 1000) / count) / 10; // Round to 3 decimals.
|
||||
}
|
||||
outputSerie.push(point);
|
||||
}
|
||||
series.clear();
|
||||
series.set("Average", outputSerie);
|
||||
|
||||
// Detect whether we went down or not on the last 10 benchmarks.
|
||||
const lastElementsCount = 3;
|
||||
const totalConsideredCount = 10;
|
||||
const lastElements = outputSerie.slice(-lastElementsCount);
|
||||
const comparedTo = outputSerie.slice(
|
||||
-totalConsideredCount,
|
||||
-lastElementsCount,
|
||||
);
|
||||
const avgLast = lastElements.reduce((a, b) => a + b) / lastElements.length;
|
||||
const avgComparedTo =
|
||||
comparedTo.reduce((a, b) => a + b) / comparedTo.length;
|
||||
const trend = avgLast - avgComparedTo;
|
||||
|
||||
if (trend > 10) {
|
||||
customColor = "#E20000";
|
||||
} else if (trend < -10) {
|
||||
customColor = "#00E200";
|
||||
}
|
||||
}
|
||||
|
||||
var options = {
|
||||
series: Array.from(series.entries()).map(([key, value]) => ({
|
||||
name: key,
|
||||
data: value,
|
||||
})),
|
||||
chart: {
|
||||
foreColor: "var(--text-bright)",
|
||||
background: "var(--background)",
|
||||
height: type === "compact" ? 200 : 600,
|
||||
type: "line",
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: "dark",
|
||||
y: {
|
||||
formatter: (value, opts) => (type === "compact" ? value + "%" : value),
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: "straight",
|
||||
width: 2,
|
||||
},
|
||||
theme: {
|
||||
palette: "palette4",
|
||||
},
|
||||
fill:
|
||||
type === "compact"
|
||||
? {
|
||||
type: "gradient",
|
||||
gradient: {
|
||||
shade: "dark",
|
||||
gradientToColors: ["#4ecdc4"],
|
||||
shadeIntensity: 1,
|
||||
type: "horizontal",
|
||||
opacityFrom: 1,
|
||||
opacityTo: 1,
|
||||
stops: [0, 100],
|
||||
},
|
||||
}
|
||||
: {},
|
||||
colors:
|
||||
type === "compact"
|
||||
? customColor
|
||||
? [customColor]
|
||||
: ["#4ecdc4"]
|
||||
: undefined,
|
||||
xaxis: {
|
||||
categories: xAxis,
|
||||
labels: {
|
||||
show: type !== "compact",
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
tickAmount: 4,
|
||||
min: type === "compact" ? 0 : undefined,
|
||||
max: type === "compact" ? 200 : undefined,
|
||||
},
|
||||
legend: {
|
||||
show: type !== "compact",
|
||||
},
|
||||
};
|
||||
|
||||
var chart = new ApexCharts(document.querySelector(targetDivID), options);
|
||||
chart.render();
|
||||
}
|
||||
Reference in New Issue
Block a user