mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Basic benchmarking
This commit is contained in:
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
FileVersion = 1
|
FileVersion = 1
|
||||||
Projects = {GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}, ScriptCore = {Path = "ScriptCore"}, NetHostBeef = {Path = "GlitchyEngine/vendor/NetHostBeef/NetHostBeef"}, "GlitchyEngine.Test" = {Path = "GlitchyEngine.Test"}, Xml-Beef = {Path = "GlitchyEngine/vendor/Xml-Beef"}}
|
Projects = {GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}, ScriptCore = {Path = "ScriptCore"}, NetHostBeef = {Path = "GlitchyEngine/vendor/NetHostBeef/NetHostBeef"}, "GlitchyEngine.Test" = {Path = "GlitchyEngine.Test"}, Xml-Beef = {Path = "GlitchyEngine/vendor/Xml-Beef"}, "GlitchyEngine.Benchmark" = {Path = "GlitchyEngine.Benchmark"}}
|
||||||
Locked = ["ImGui"]
|
Locked = ["ImGui"]
|
||||||
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore", "GlitchyEngine.Test"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq", "NetHostBeef", "Xml-Beef"], Beef = ["Beefy2D", "corlib", "MiniZ"]}
|
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore", "GlitchyEngine.Test", "GlitchyEngine.Benchmark"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq", "NetHostBeef", "Xml-Beef"], Beef = ["Beefy2D", "corlib"]}
|
||||||
|
|
||||||
[Workspace]
|
[Workspace]
|
||||||
StartupProject = "GlitchyEditor"
|
StartupProject = "GlitchyEditor"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FileVersion = 1
|
||||||
|
Dependencies = {corlib = "*", "Beef.Linq" = "*", GlitchyEngine = "*"}
|
||||||
|
|
||||||
|
[Project]
|
||||||
|
Name = "GlitchyEngine.Benchmark"
|
||||||
|
StartupObject = "GlitchyEngine.Benchmark.BenchmarkApp"
|
||||||
|
|
||||||
|
[Configs.Debug.Win64]
|
||||||
|
DebugWorkingDirectory = "D:\\Development\\Projects\\Beef\\GlitchyEngine\\GlitchyEditor"
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
using GlitchyEngine.World;
|
||||||
|
using GlitchyEngine.Scripting;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
namespace GlitchyEngine.Benchmark;
|
||||||
|
|
||||||
|
class BasicBenchmark
|
||||||
|
{
|
||||||
|
public static void Setup()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Starting up ScriptEngine...");
|
||||||
|
Console.WriteLine(Directory.GetCurrentDirectory(.. scope .()));
|
||||||
|
ScriptEngine.Init();
|
||||||
|
|
||||||
|
ScriptEngine.SetAppAssemblyPath(@"..\GlitchyEngine.Test\content\TestProject\.cache\bin\TestProject.dll");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void Shutdown()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Shutting down ScriptEngine...");
|
||||||
|
ScriptEngine.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public static void BenchmarkSerializeEmptyScene()
|
||||||
|
{
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
Scene scene = new Scene();
|
||||||
|
defer scene.ReleaseRef();
|
||||||
|
|
||||||
|
ScriptInstanceSerializer serializer = scope .();
|
||||||
|
|
||||||
|
scene.Start(true, true);
|
||||||
|
|
||||||
|
Stopwatch time = scope Stopwatch(startNow: true);
|
||||||
|
|
||||||
|
serializer.SerializeScriptInstances();
|
||||||
|
|
||||||
|
time.Stop();
|
||||||
|
|
||||||
|
Console.WriteLine($"Serialized in {time.ElapsedMicroseconds}μs ({time.ElapsedMilliseconds}ms)");
|
||||||
|
|
||||||
|
scene.Stop();
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static void BenchmarkSerializer()
|
||||||
|
{
|
||||||
|
// Before benchmark
|
||||||
|
Setup();
|
||||||
|
|
||||||
|
BenchmarkResult resultCollector = scope .();
|
||||||
|
|
||||||
|
Scene scene = new Scene();
|
||||||
|
defer scene.ReleaseRef();
|
||||||
|
|
||||||
|
ScriptInstanceSerializer serializer = null;
|
||||||
|
|
||||||
|
let benchmark = scope Benchmark();
|
||||||
|
benchmark.BeforeAll = scope [&]() => {
|
||||||
|
scene.Start(true, true);
|
||||||
|
};
|
||||||
|
benchmark.BeforeRun = scope [&]() => {
|
||||||
|
serializer = new .();
|
||||||
|
};
|
||||||
|
benchmark.Run = scope [&]() => {
|
||||||
|
serializer.SerializeScriptInstances();
|
||||||
|
};
|
||||||
|
benchmark.AfterRun = scope [&]() => {
|
||||||
|
delete serializer;
|
||||||
|
};
|
||||||
|
benchmark.AfterAll = scope [&]() => {
|
||||||
|
scene.Stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine("Benchmark empty scene:");
|
||||||
|
{
|
||||||
|
benchmark.Name = "Empty Scene";
|
||||||
|
benchmark.Run(resultCollector);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Entity> references = new List<Entity>(1000);
|
||||||
|
defer delete references;
|
||||||
|
|
||||||
|
void BenchmarkTrivialEntity(int entityCount)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Benchmark {entityCount} Trivial Entity:");
|
||||||
|
|
||||||
|
for (int i < entityCount)
|
||||||
|
{
|
||||||
|
readonly Entity entity = scene.CreateEntity("Test");
|
||||||
|
let script = entity.AddComponent<ScriptComponent>();
|
||||||
|
script.ScriptClassName = "TestProject.TestScript";
|
||||||
|
|
||||||
|
references.Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark.Name = scope $"Trivial Entity {entityCount}";
|
||||||
|
benchmark.Run(resultCollector);
|
||||||
|
|
||||||
|
for (Entity e in references)
|
||||||
|
{
|
||||||
|
scene.DestroyEntity(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
references.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
BenchmarkTrivialEntity(1);
|
||||||
|
BenchmarkTrivialEntity(10);
|
||||||
|
BenchmarkTrivialEntity(100);
|
||||||
|
BenchmarkTrivialEntity(1000);
|
||||||
|
|
||||||
|
void BenchmarkLargeEntity(int entityCount)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Benchmark {entityCount} Large Entity:");
|
||||||
|
|
||||||
|
for (int i < entityCount)
|
||||||
|
{
|
||||||
|
readonly Entity entity = scene.CreateEntity("Test");
|
||||||
|
let script = entity.AddComponent<ScriptComponent>();
|
||||||
|
script.ScriptClassName = "TestProject.SerializationTest";
|
||||||
|
|
||||||
|
references.Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark.Name = scope $"Large Entity {entityCount}";
|
||||||
|
benchmark.Run(resultCollector);
|
||||||
|
|
||||||
|
for (Entity e in references)
|
||||||
|
{
|
||||||
|
scene.DestroyEntity(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
references.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
BenchmarkLargeEntity(1);
|
||||||
|
BenchmarkLargeEntity(10);
|
||||||
|
BenchmarkLargeEntity(100);
|
||||||
|
BenchmarkLargeEntity(1000);
|
||||||
|
|
||||||
|
// After Bench
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
String output = scope .();
|
||||||
|
resultCollector.PrintStats(output);
|
||||||
|
File.WriteAllText("test.log", output);
|
||||||
|
|
||||||
|
Console.WriteLine(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Benchmark;
|
||||||
|
|
||||||
|
class BenchmarkResult
|
||||||
|
{
|
||||||
|
public class BenchmarkRun
|
||||||
|
{
|
||||||
|
public String Name { get; private set; } ~ delete:append _;
|
||||||
|
public TimeSpan[] RunDurations { get; private set; } ~ delete:append _;
|
||||||
|
|
||||||
|
public int RunCount => RunDurations.Count;
|
||||||
|
|
||||||
|
public TimeSpan TotalRunTime { get; private set; }
|
||||||
|
public TimeSpan MeanRunTime { get; private set; }
|
||||||
|
public TimeSpan RuntimeStdDev { get; private set; }
|
||||||
|
|
||||||
|
[AllowAppend]
|
||||||
|
public this(StringView name, int runCount)
|
||||||
|
{
|
||||||
|
String appendedName = append String(name);
|
||||||
|
TimeSpan[] appendedRunDurations = append TimeSpan[runCount];
|
||||||
|
|
||||||
|
Name = appendedName;
|
||||||
|
RunDurations = appendedRunDurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CalculateStats()
|
||||||
|
{
|
||||||
|
TotalRunTime = RunDurations.Sum();
|
||||||
|
MeanRunTime = TimeSpan(TotalRunTime.Ticks / RunCount);
|
||||||
|
|
||||||
|
int64 variance = RunDurations.Select(scope (d) => {
|
||||||
|
TimeSpan delta = d - MeanRunTime;
|
||||||
|
return (delta * delta).Ticks / RunCount;
|
||||||
|
}).Sum();
|
||||||
|
RuntimeStdDev = TimeSpan((int64)Math.Sqrt((double)variance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private append List<BenchmarkRun> _runData ~ ClearAndDeleteItems!(_);
|
||||||
|
|
||||||
|
public this()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BenchmarkRun NewRun(StringView name, int iterations)
|
||||||
|
{
|
||||||
|
BenchmarkRun newRunResult = new BenchmarkRun(name, iterations);
|
||||||
|
_runData.Add(newRunResult);
|
||||||
|
|
||||||
|
return newRunResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TimeUnit
|
||||||
|
{
|
||||||
|
Milliseconds,
|
||||||
|
Seconds,
|
||||||
|
Minutes,
|
||||||
|
Hours,
|
||||||
|
Days
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeUnit FigureOutUnits<T>(T timeData) where T : concrete, IEnumerable<TimeSpan>
|
||||||
|
{
|
||||||
|
TimeSpan shortestMeanTime = _runData.Select(scope (s) => s.MeanRunTime).Min();
|
||||||
|
|
||||||
|
if (shortestMeanTime.TotalDays > 2)
|
||||||
|
{
|
||||||
|
return .Days;
|
||||||
|
}
|
||||||
|
else if (shortestMeanTime.TotalHours > 2)
|
||||||
|
{
|
||||||
|
return .Hours;
|
||||||
|
}
|
||||||
|
else if (shortestMeanTime.TotalMinutes > 5)
|
||||||
|
{
|
||||||
|
return .Minutes;
|
||||||
|
}
|
||||||
|
else if (shortestMeanTime.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
return .Seconds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return .Milliseconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintTimeSpan(TimeSpan timeToPrint, TimeUnit unitToPrint, String outBuffer)
|
||||||
|
{
|
||||||
|
switch (unitToPrint)
|
||||||
|
{
|
||||||
|
case .Milliseconds:
|
||||||
|
outBuffer.AppendF($"{timeToPrint.TotalMilliseconds:f3} ms");
|
||||||
|
case .Seconds:
|
||||||
|
outBuffer.AppendF($"{timeToPrint.TotalSeconds:f3} s");
|
||||||
|
case .Minutes:
|
||||||
|
outBuffer.AppendF($"{timeToPrint.TotalMinutes:f3} m");
|
||||||
|
case .Hours:
|
||||||
|
outBuffer.AppendF($"{timeToPrint.TotalHours:f3} h");
|
||||||
|
case .Days:
|
||||||
|
outBuffer.AppendF($"{timeToPrint.TotalDays:f3} d");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrintStats(String outString, bool printHeader = true, TimeUnit? forcedTimeUnit = null)
|
||||||
|
{
|
||||||
|
for (BenchmarkRun run in _runData)
|
||||||
|
{
|
||||||
|
run.CalculateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeUnit printedUnit = forcedTimeUnit ?? FigureOutUnits(_runData.Select(scope (s) => s.MeanRunTime));
|
||||||
|
|
||||||
|
var widths = (NameColumn: 8, TotalTimeColumn: 7, MeanTimeColumn: 7, StdDevColumn: 7);
|
||||||
|
|
||||||
|
SimpleStringList preparedStrings = scope .();
|
||||||
|
for (BenchmarkRun run in _runData)
|
||||||
|
{
|
||||||
|
// Print Name
|
||||||
|
widths.NameColumn = Math.Max(widths.NameColumn, run.Name.Length);
|
||||||
|
|
||||||
|
// Print Total Time
|
||||||
|
StringView totalRunTimeView = preparedStrings.Add(scope (s) => PrintTimeSpan(run.TotalRunTime, printedUnit, s));
|
||||||
|
widths.TotalTimeColumn = Math.Max(widths.TotalTimeColumn, totalRunTimeView.Length);
|
||||||
|
|
||||||
|
// Print Total Time
|
||||||
|
StringView meanRunTimeView = preparedStrings.Add(scope (s) => PrintTimeSpan(run.MeanRunTime, printedUnit, s));
|
||||||
|
widths.MeanTimeColumn = Math.Max(widths.MeanTimeColumn, meanRunTimeView.Length);
|
||||||
|
|
||||||
|
// Print Standard Deviation
|
||||||
|
StringView stdDevView = preparedStrings.Add(scope (s) => PrintTimeSpan(run.RuntimeStdDev, printedUnit, s));
|
||||||
|
widths.StdDevColumn = Math.Max(widths.StdDevColumn, stdDevView.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
String lineFormat = scope $"| {{, -{widths.NameColumn}}} | {{, {widths.TotalTimeColumn}}} | {{, {widths.MeanTimeColumn}}} | {{, {widths.StdDevColumn}}} |\n";
|
||||||
|
|
||||||
|
if (printHeader)
|
||||||
|
{
|
||||||
|
int lengthBefore = outString.Length;
|
||||||
|
outString.AppendF(lineFormat, Formatter.Center("Benchmark", widths.NameColumn), Formatter.Center("Total", widths.TotalTimeColumn),
|
||||||
|
Formatter.Center("Mean", widths.MeanTimeColumn), Formatter.Center("Std", widths.StdDevColumn));
|
||||||
|
int lineLength = outString.Length - lengthBefore;
|
||||||
|
outString.Append('-', lineLength - 1);
|
||||||
|
outString.Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
int preparedStringIndex = 0;
|
||||||
|
for (BenchmarkRun run in _runData)
|
||||||
|
{
|
||||||
|
outString.AppendF(lineFormat, run.Name, preparedStrings[preparedStringIndex++], preparedStrings[preparedStringIndex++], preparedStrings[preparedStringIndex++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Benchmark
|
||||||
|
{
|
||||||
|
private append String _name = .();
|
||||||
|
|
||||||
|
public StringView Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set => _name.Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action BeforeAll;
|
||||||
|
public Action BeforeRun;
|
||||||
|
public Action Run;
|
||||||
|
public Action AfterRun;
|
||||||
|
public Action AfterAll;
|
||||||
|
|
||||||
|
public void Run(BenchmarkResult resultsCollector, int warmupRuns = 10, int runs = 100)
|
||||||
|
{
|
||||||
|
let resultData = resultsCollector.NewRun(Name, runs);
|
||||||
|
|
||||||
|
BeforeAll?.Invoke();
|
||||||
|
|
||||||
|
Stopwatch watch = scope .();
|
||||||
|
|
||||||
|
watch.Start();
|
||||||
|
for (int i < warmupRuns)
|
||||||
|
{
|
||||||
|
Console.Write($"Warmup run {i + 1}/{warmupRuns}...");
|
||||||
|
Console.CursorLeft = 0;
|
||||||
|
//TimeSpan duration = DoRun();
|
||||||
|
//Console.WriteLine($"Warmup run {i + 1} finished after {duration.TotalMilliseconds}ms.");
|
||||||
|
}
|
||||||
|
watch.Stop();
|
||||||
|
Console.WriteLine($"Warmup finished after {watch.Elapsed.TotalMilliseconds}ms");
|
||||||
|
|
||||||
|
for (int i < runs)
|
||||||
|
{
|
||||||
|
Console.Write($"Starting run {i + 1}/{runs}...");
|
||||||
|
Console.CursorLeft = 0;
|
||||||
|
|
||||||
|
watch.Restart();
|
||||||
|
resultData.RunDurations[i] = DoRun();
|
||||||
|
watch.Stop();
|
||||||
|
|
||||||
|
//Console.WriteLine($"Run {i + 1}: {_runDurations[i]} (total: {watch.Elapsed.TotalMilliseconds}ms).");
|
||||||
|
}
|
||||||
|
|
||||||
|
resultData.CalculateStats();
|
||||||
|
|
||||||
|
Console.WriteLine(scope $"Finished: Total ({runs} runs) {resultData.TotalRunTime.TotalMilliseconds}ms | Mean: {resultData.MeanRunTime.TotalMilliseconds}ms | Std Dev: {resultData.RuntimeStdDev.TotalMilliseconds}ms");
|
||||||
|
|
||||||
|
AfterAll?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeSpan DoRun()
|
||||||
|
{
|
||||||
|
Stopwatch watch = scope .();
|
||||||
|
BeforeRun?.Invoke();
|
||||||
|
|
||||||
|
watch.Start();
|
||||||
|
Run.Invoke();
|
||||||
|
watch.Stop();
|
||||||
|
|
||||||
|
AfterRun?.Invoke();
|
||||||
|
|
||||||
|
return watch.Elapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Benchmark;
|
||||||
|
|
||||||
|
class BenchmarkApp
|
||||||
|
{
|
||||||
|
public static void Main(String[] args)
|
||||||
|
{
|
||||||
|
BasicBenchmark.BenchmarkSerializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a stub implementation for CreateApplication, so that the linker is happy.
|
||||||
|
[Export, LinkName("CreateApplication")]
|
||||||
|
public static Application CreateApplication(String[] args)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Benchmark;
|
||||||
|
|
||||||
|
static class Formatter
|
||||||
|
{
|
||||||
|
public static CenterFormatter<T> Center<T>(T value, int width, bool leftAlignWhenUneven = true)
|
||||||
|
{
|
||||||
|
return CenterFormatter<T>(value, width, leftAlignWhenUneven);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CenterFormatter<T> : IFormattable
|
||||||
|
{
|
||||||
|
private T _value;
|
||||||
|
private int _width;
|
||||||
|
private bool _leftAlignWhenUneven;
|
||||||
|
|
||||||
|
public this(T value, int width, bool leftAlignWhenUneven = true)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_width = width;
|
||||||
|
_leftAlignWhenUneven = leftAlignWhenUneven;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||||
|
{
|
||||||
|
String buffer = scope String(128);
|
||||||
|
IFormattable formattableArg = _value as IFormattable;
|
||||||
|
if (formattableArg != null)
|
||||||
|
formattableArg.ToString(buffer, format, formatProvider);
|
||||||
|
else if (_value != null)
|
||||||
|
_value.ToString(buffer);
|
||||||
|
else
|
||||||
|
buffer.Append("null");
|
||||||
|
|
||||||
|
int missingSpaceCount = _width - buffer.Length;
|
||||||
|
|
||||||
|
int widthAfterLeftPad = _width - missingSpaceCount / 2;
|
||||||
|
// If the number of spaces is uneven and we want to left align (have more space right) we need to pad one less on the left
|
||||||
|
if (_leftAlignWhenUneven && missingSpaceCount % 2 == 1)
|
||||||
|
widthAfterLeftPad -= 1;
|
||||||
|
|
||||||
|
buffer..PadLeft(widthAfterLeftPad);
|
||||||
|
outString..Append(buffer).PadRight(_width, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Benchmark;
|
||||||
|
|
||||||
|
class SimpleStringList
|
||||||
|
{
|
||||||
|
private append String _buffer = .();
|
||||||
|
private append List<(int start, int length)> _views = .();
|
||||||
|
|
||||||
|
public StringView this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
let (start, length) = _views[index];
|
||||||
|
return StringView(_buffer, start, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringView Add(StringView text)
|
||||||
|
{
|
||||||
|
int lengthBefore = _buffer.Length;
|
||||||
|
|
||||||
|
_buffer.Append(text);
|
||||||
|
|
||||||
|
StringView newStringView = StringView(_buffer, lengthBefore);
|
||||||
|
_views.Add((lengthBefore, newStringView.Length));
|
||||||
|
|
||||||
|
// Append a null terminator to ensure that every string can easily be a C string.
|
||||||
|
// Append after creating string view so it isn't part of the view.
|
||||||
|
_buffer.Append('\0');
|
||||||
|
|
||||||
|
return newStringView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void CustomToString(String buffer);
|
||||||
|
|
||||||
|
// Allows adding a string directly into the buffer, using the internal buffer as target for e.g. ToString-opeations.
|
||||||
|
public StringView Add(CustomToString toString)
|
||||||
|
{
|
||||||
|
int lengthBefore = _buffer.Length;
|
||||||
|
|
||||||
|
toString(_buffer);
|
||||||
|
|
||||||
|
StringView newStringView = StringView(_buffer, lengthBefore);
|
||||||
|
_views.Add((lengthBefore, newStringView.Length));
|
||||||
|
|
||||||
|
// Append a null terminator to ensure that every string can easily be a C string.
|
||||||
|
// Append after creating string view so it isn't part of the view.
|
||||||
|
_buffer.Append('\0');
|
||||||
|
|
||||||
|
return newStringView;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,10 @@ Dependencies = {corlib = "*", GlitchyEngine = "*", corlib = "*"}
|
|||||||
|
|
||||||
[Project]
|
[Project]
|
||||||
Name = "GlitchyEngine.Test"
|
Name = "GlitchyEngine.Test"
|
||||||
TargetType = "BeefTest"
|
StartupObject = "GlitchyEngine.Test.TestApp"
|
||||||
StartupObject = "GlitchyEngine.Test.Program"
|
|
||||||
|
|
||||||
[Configs.Debug.Win64]
|
[Configs.Debug.Win64]
|
||||||
DebugWorkingDirectory = ""
|
DebugWorkingDirectory = "D:\\Development\\Projects\\Beef\\GlitchyEngine\\GlitchyEditor"
|
||||||
|
|
||||||
[Configs.Test.Win64]
|
[Configs.Test.Win64]
|
||||||
DebugWorkingDirectory = "$(WorkspaceDir)\\GlitchyEditor"
|
DebugWorkingDirectory = "$(WorkspaceDir)\\GlitchyEditor"
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RiderProjectSettingsUpdater">
|
||||||
|
<option name="singleClickDiffPreview" value="1" />
|
||||||
|
<option name="unhandledExceptionsIgnoreList" value="1" />
|
||||||
|
<option name="vcsConfiguration" value="3" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoGeneratedRunConfigurationManager">
|
||||||
|
<projectFile profileName="Start Editor">../../../ScriptCore/ScriptCore.csproj</projectFile>
|
||||||
|
</component>
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="91b81792-3965-45fc-b023-6a7c118ee745" name="Changes" comment="" />
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectColorInfo"><![CDATA[{
|
||||||
|
"customColor": "",
|
||||||
|
"associatedIndex": 3
|
||||||
|
}]]></component>
|
||||||
|
<component name="ProjectId" id="31KeCm9hymLojzNB0dAwlIYVNgh" />
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RunManager">
|
||||||
|
<configuration name="ScriptCore: Start Editor" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
|
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/../../../ScriptCore/ScriptCore.csproj" />
|
||||||
|
<option name="LAUNCH_PROFILE_TFM" value="net9.0" />
|
||||||
|
<option name="LAUNCH_PROFILE_NAME" value="Start Editor" />
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="GENERATE_APPLICATIONHOST_CONFIG" value="1" />
|
||||||
|
<option name="SHOW_IIS_EXPRESS_OUTPUT" value="0" />
|
||||||
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="91b81792-3965-45fc-b023-6a7c118ee745" name="Changes" comment="" />
|
||||||
|
<created>1755277067318</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1755277067318</updated>
|
||||||
|
<workItem from="1755277069029" duration="595000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
|
||||||
|
<component name="VcsManagerConfiguration">
|
||||||
|
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="XDebuggerManager">
|
||||||
|
<breakpoint-manager>
|
||||||
|
<breakpoints>
|
||||||
|
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||||
|
<properties exception="System.OperationCanceledException" breakIfHandledByOtherCode="false" displayValue="System.OperationCanceledException" />
|
||||||
|
<option name="timeStamp" value="1" />
|
||||||
|
</breakpoint>
|
||||||
|
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||||
|
<properties exception="System.Threading.Tasks.TaskCanceledException" breakIfHandledByOtherCode="false" displayValue="System.Threading.Tasks.TaskCanceledException" />
|
||||||
|
<option name="timeStamp" value="2" />
|
||||||
|
</breakpoint>
|
||||||
|
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||||
|
<properties exception="System.Threading.ThreadAbortException" breakIfHandledByOtherCode="false" displayValue="System.Threading.ThreadAbortException" />
|
||||||
|
<option name="timeStamp" value="3" />
|
||||||
|
</breakpoint>
|
||||||
|
</breakpoints>
|
||||||
|
</breakpoint-manager>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -12,8 +12,8 @@ class ScriptEngineTestBase
|
|||||||
{
|
{
|
||||||
public static void Setup()
|
public static void Setup()
|
||||||
{
|
{
|
||||||
Debug.WriteLine("Starting up ScriptEngine...");
|
Console.WriteLine("Starting up ScriptEngine...");
|
||||||
Debug.WriteLine(Directory.GetCurrentDirectory(.. scope .()));
|
Console.WriteLine(Directory.GetCurrentDirectory(.. scope .()));
|
||||||
ScriptEngine.Init();
|
ScriptEngine.Init();
|
||||||
|
|
||||||
ScriptEngine.SetAppAssemblyPath(@"..\GlitchyEngine.Test\content\TestProject\.cache\bin\TestProject.dll");
|
ScriptEngine.SetAppAssemblyPath(@"..\GlitchyEngine.Test\content\TestProject\.cache\bin\TestProject.dll");
|
||||||
@@ -21,7 +21,7 @@ class ScriptEngineTestBase
|
|||||||
|
|
||||||
protected static void Shutdown()
|
protected static void Shutdown()
|
||||||
{
|
{
|
||||||
Debug.WriteLine("Shutting down ScriptEngine...");
|
Console.WriteLine("Shutting down ScriptEngine...");
|
||||||
ScriptEngine.Shutdown();
|
ScriptEngine.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,13 +77,15 @@ class ScriptEngineTestBase
|
|||||||
|
|
||||||
time.Stop();
|
time.Stop();
|
||||||
|
|
||||||
Debug.WriteLine($"Serialized in {time.ElapsedMicroseconds}μs ({time.ElapsedMilliseconds}ms)");
|
Console.WriteLine($"Serialized in {time.ElapsedMicroseconds}μs ({time.ElapsedMilliseconds}ms)");
|
||||||
|
|
||||||
scene.Stop();
|
scene.Stop();
|
||||||
|
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String output = new .() ~ delete _;
|
||||||
|
|
||||||
class Benchmark
|
class Benchmark
|
||||||
{
|
{
|
||||||
public Action BeforeAll;
|
public Action BeforeAll;
|
||||||
@@ -103,25 +105,25 @@ class ScriptEngineTestBase
|
|||||||
watch.Start();
|
watch.Start();
|
||||||
for (int i < warmupRuns)
|
for (int i < warmupRuns)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Warmup run {i + 1}/{warmupRuns}...");
|
Console.WriteLine($"Warmup run {i + 1}/{warmupRuns}...");
|
||||||
TimeSpan duration = DoRun();
|
TimeSpan duration = DoRun();
|
||||||
Debug.WriteLine($"Warmup run {i + 1} finished after {duration.TotalMilliseconds}ms.");
|
Console.WriteLine($"Warmup run {i + 1} finished after {duration.TotalMilliseconds}ms.");
|
||||||
}
|
}
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
Debug.WriteLine($"Warmup finished after {watch.Elapsed.TotalMilliseconds}ms");
|
Console.WriteLine($"Warmup finished after {watch.Elapsed.TotalMilliseconds}ms");
|
||||||
|
|
||||||
_runDurations = new TimeSpan[runs];
|
_runDurations = new TimeSpan[runs];
|
||||||
defer delete _runDurations;
|
defer delete _runDurations;
|
||||||
|
|
||||||
for (int i < runs)
|
for (int i < runs)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Starting run {i + 1}/{runs}...");
|
Console.WriteLine($"Starting run {i + 1}/{runs}...");
|
||||||
|
|
||||||
watch.Restart();
|
watch.Restart();
|
||||||
_runDurations[i] = DoRun();
|
_runDurations[i] = DoRun();
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
|
||||||
Debug.WriteLine($"Run {i + 1}: {_runDurations[i]} (total: {watch.Elapsed.TotalMilliseconds}ms).");
|
Console.WriteLine($"Run {i + 1}: {_runDurations[i]} (total: {watch.Elapsed.TotalMilliseconds}ms).");
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeSpan totalRunTime = _runDurations.Sum();
|
TimeSpan totalRunTime = _runDurations.Sum();
|
||||||
@@ -130,10 +132,12 @@ class ScriptEngineTestBase
|
|||||||
double varianceMs = _runDurations.Select(scope (d) => Math.Pow((d - meanRunTime).TotalMilliseconds, 2)).Sum() / runs;
|
double varianceMs = _runDurations.Select(scope (d) => Math.Pow((d - meanRunTime).TotalMilliseconds, 2)).Sum() / runs;
|
||||||
TimeSpan stdDeviation = TimeSpan.FromMilliseconds(Math.Sqrt(varianceMs));
|
TimeSpan stdDeviation = TimeSpan.FromMilliseconds(Math.Sqrt(varianceMs));
|
||||||
|
|
||||||
Debug.WriteLine($"Results:");
|
Console.WriteLine($"Results:");
|
||||||
Debug.WriteLine($"Total Time ({runs} runs): {totalRunTime.TotalMilliseconds}ms");
|
Console.WriteLine($"Total Time ({runs} runs): {totalRunTime.TotalMilliseconds}ms");
|
||||||
Debug.WriteLine($"Average run Time: {meanRunTime.TotalMilliseconds}ms");
|
Console.WriteLine($"Average run Time: {meanRunTime.TotalMilliseconds}ms");
|
||||||
Debug.WriteLine($"Std deviation: {stdDeviation.TotalMilliseconds}ms");
|
Console.WriteLine($"Std deviation: {stdDeviation.TotalMilliseconds}ms");
|
||||||
|
|
||||||
|
output.AppendF($"Total: {totalRunTime.TotalMilliseconds}ms | Mean: {meanRunTime.TotalMilliseconds}ms Std: {stdDeviation.TotalMilliseconds}ms\n");
|
||||||
|
|
||||||
AfterAll?.Invoke();
|
AfterAll?.Invoke();
|
||||||
}
|
}
|
||||||
@@ -162,7 +166,6 @@ class ScriptEngineTestBase
|
|||||||
Scene scene = new Scene();
|
Scene scene = new Scene();
|
||||||
defer scene.ReleaseRef();
|
defer scene.ReleaseRef();
|
||||||
|
|
||||||
|
|
||||||
ScriptInstanceSerializer serializer = null;
|
ScriptInstanceSerializer serializer = null;
|
||||||
|
|
||||||
let benchmark = scope Benchmark();
|
let benchmark = scope Benchmark();
|
||||||
@@ -182,7 +185,8 @@ class ScriptEngineTestBase
|
|||||||
scene.Stop();
|
scene.Stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
Debug.WriteLine("Benchmark empty scene:");
|
Console.WriteLine("Benchmark empty scene:");
|
||||||
|
output.AppendF("Benchmark empty scene:");
|
||||||
{
|
{
|
||||||
benchmark.Run();
|
benchmark.Run();
|
||||||
}
|
}
|
||||||
@@ -192,7 +196,8 @@ class ScriptEngineTestBase
|
|||||||
|
|
||||||
void BenchmarkTrivialEntity(int entityCount)
|
void BenchmarkTrivialEntity(int entityCount)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Benchmark {entityCount} Trivial Entity:");
|
Console.WriteLine($"Benchmark {entityCount} Trivial Entity:");
|
||||||
|
output.AppendF($"Benchmark {entityCount} Trivial Entity:");
|
||||||
|
|
||||||
for (int i < entityCount)
|
for (int i < entityCount)
|
||||||
{
|
{
|
||||||
@@ -220,7 +225,8 @@ class ScriptEngineTestBase
|
|||||||
|
|
||||||
void BenchmarkLargeEntity(int entityCount)
|
void BenchmarkLargeEntity(int entityCount)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Benchmark {entityCount} Large Entity:");
|
Console.WriteLine($"Benchmark {entityCount} Large Entity:");
|
||||||
|
output.AppendF($"Benchmark {entityCount} Large Entity:");
|
||||||
|
|
||||||
for (int i < entityCount)
|
for (int i < entityCount)
|
||||||
{
|
{
|
||||||
@@ -248,5 +254,7 @@ class ScriptEngineTestBase
|
|||||||
|
|
||||||
// After Bench
|
// After Bench
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
||||||
|
File.WriteAllText("test.log", output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using GlitchyEngine.Test.Base;
|
||||||
namespace GlitchyEngine.Test;
|
namespace GlitchyEngine.Test;
|
||||||
|
|
||||||
class TestApp
|
class TestApp
|
||||||
{
|
{
|
||||||
/// Provide a stup implementation for CreateApplication, so that the linker is happy.
|
public static void Main(String[] args)
|
||||||
|
{
|
||||||
|
ScriptEngineTestBase.BenchmarkSerializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a stub implementation for CreateApplication, so that the linker is happy.
|
||||||
[Export, LinkName("CreateApplication")]
|
[Export, LinkName("CreateApplication")]
|
||||||
public static Application CreateApplication(String[] args)
|
public static Application CreateApplication(String[] args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,17 +122,17 @@ namespace GlitchyEngine.World
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_font == null)
|
/*if (_font == null)
|
||||||
_font = new Font(@"C:\Windows\Fonts\arial.ttf", 24);
|
_font = new Font(@"C:\Windows\Fonts\arial.ttf", 24);
|
||||||
else
|
else
|
||||||
_font..AddRef();
|
_font..AddRef();*/
|
||||||
|
|
||||||
//_font = new Font(@"C:\Windows\Fonts\Cascadia Code.ttf", 24);
|
//_font = new Font(@"C:\Windows\Fonts\Cascadia Code.ttf", 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ~this()
|
public ~this()
|
||||||
{
|
{
|
||||||
if (_font.ReleaseRefGetCount() == 0)
|
if (_font?.ReleaseRefGetCount() == 0)
|
||||||
{
|
{
|
||||||
_font = null;
|
_font = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user