Skip to content

Commit

Permalink
Port GDScript benchmarks to C# (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
OverloadedOrama authored Aug 4, 2024
1 parent b8827a4 commit bfd8458
Show file tree
Hide file tree
Showing 11 changed files with 1,091 additions and 0 deletions.
80 changes: 80 additions & 0 deletions benchmarks/csharp/Alloc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using Godot;

public partial class Alloc : Benchmark
{
const int ITERATIONS = 100_000;

void BenchmarkDeepTree()
{
Node rt = new Node();
for (int i = 0; i < ITERATIONS; i++)
{
Node n = new Node();
n.AddChild(rt);
rt = n;
}

// Avoid triggering a stack overflow with rt.Free()
while (rt.GetChildCount() != 0)
{
Node n = rt.GetChild(0);
rt.RemoveChild(n);
rt.Free();
rt = n;
}
rt.Free();
}

void BenchmarkWideTree()
{
Node rt = new Node();
for (int i = 0; i < ITERATIONS; i++)
{
rt.AddChild(new Node());
}
rt.Free();
}

void BenchmarkFragmentation()
{
Node top = new Node();
for (int i = 0; i < 5; i++)
{
top.AddChild(new Node());
}

for (int k = 0; k < 10; k++) {
for (int i = 0; i < ITERATIONS; i++)
{
// Attempt to scatter children in memory by assigning newly created nodes to a random parent
int idx = (int)GD.Randi() % top.GetChildCount();
top.GetChild(idx).AddChild(new Node());
}

Node tmp = top.GetChild(0);
top.RemoveChild(tmp);
// Since nodes in the tree are scattered in memory,
// freeing subtrees this way should maximize fragmentation.
tmp.Free();
top.AddChild(new Node());
//GD.Print("Iteration %d: %.3f MB" % [k, Performance.get_monitor(Performance.MEMORY_STATIC) / 1e6])
}

top.Free();
}

void BenchmarkDuplicate()
{
Node rt = new Node();
for (int i = 0; i < 16; i++)
{
Node n = new Node();
n.AddChild(rt.Duplicate());
n.AddChild(rt.Duplicate());
rt.Free();
rt = n;
//GD.Print("Iteration %d: %.3f MB" % [i, Performance.get_monitor(Performance.MEMORY_STATIC) / 1e6])
}
rt.Free();
}
}
77 changes: 77 additions & 0 deletions benchmarks/csharp/BinaryTrees.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Godot;

public partial class BinaryTrees : Benchmark
{
// Based on https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/binarytrees/1.cs
class TreeNode
{
public TreeNode left;
public TreeNode right;

TreeNode(TreeNode left = null, TreeNode right = null)
{
this.left = left;
this.right = right;
}

internal static TreeNode Create(int d)
{
return d == 0 ? new TreeNode()
: new TreeNode(Create(d - 1), Create(d - 1));
}

internal int Check()
{
int c = 1;
if (right != null)
{
c += right.Check();
}
if (left != null)
{
c += left.Check();
}
return c;
}
}

const int MinDepth = 4;
public void CalculateBinaryTrees(int input)
{
int maxDepth = Mathf.Max(MinDepth + 2, input);

int stretchDepth = maxDepth + 1;
GD.Print($"stretch tree of depth {stretchDepth}\t check: {TreeNode.Create(stretchDepth).Check()}");

TreeNode longLivedTree = TreeNode.Create(maxDepth);
int maxPlusMinDepth = maxDepth + MinDepth;
for (int depth = MinDepth; depth < maxDepth; depth += 2)
{
int iterations = 1 << (maxPlusMinDepth - depth);
int check = 0;
for (int i = 0; i < iterations; i++)
{
check += TreeNode.Create(depth).Check();
}

GD.Print($"{iterations}\t trees of depth {depth}\t check: {check}");
}

GD.Print($"long lived tree of depth {maxDepth}\t check: {longLivedTree.Check()}");
}

public void BenchmarkBinaryTrees13()
{
CalculateBinaryTrees(13);
}

public void BenchmarkBinaryTrees15()
{
CalculateBinaryTrees(15);
}

public void BenchmarkBinaryTrees18()
{
CalculateBinaryTrees(18);
}
}
9 changes: 9 additions & 0 deletions benchmarks/csharp/HelloWorld.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Godot;

public partial class HelloWorld : Benchmark
{
public void BenchmarkHelloWorld()
{
GD.Print("Hello world!");
}
}
15 changes: 15 additions & 0 deletions benchmarks/csharp/LambdaPerformance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

public partial class LambdaPerformance : Benchmark
{
const int ITERATIONS = 1_000_000;
Action lambda = () => { };

public void BenchmarkLambdaCall()
{
for(int i = 0; i < ITERATIONS; i++)
{
lambda();
}
}
}
70 changes: 70 additions & 0 deletions benchmarks/csharp/MandelbrotSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Godot;

public partial class MandelbrotSet : Benchmark
{
const int WIDTH = 600;
const int HEIGHT = 400;
const int MAX_ITERATION = 1000;

private Color HSV(float hue, float sat, float value)
{
hue = Mathf.PosMod(hue, 360.0f);
int h = Mathf.FloorToInt(hue) / 60;
float f = hue / 60.0f - h;
float p = value * (1.0f - sat);
float q = value * (1.0f - sat * f);
float t = value * (1.0f - sat * (1.0f - f));
if (h == 0 || h == 6)
return new Color(value, t, p);
if (h == 1)
return new Color(q, value, p);
if (h == 2)
return new Color(p, value, t);
if (h == 3)
return new Color(p, q, value);
if (h == 4)
return new Color(t, p, value);
return new Color(value, p, q);
}

// Algorithm from
// https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Optimized_escape_time_algorithms
private void mandelbrot_set(int width, int height, int maxIteration)
{
Image image = Image.CreateEmpty(width, height, false, Image.Format.Rgb8);
float ratio = (float)width / (float)height;
float xRange = 3.6f;
float yRange = xRange / ratio;
float minX = -xRange / 2.0f;
float maxY = yRange / 2.0f;
for (int x = 0; x < image.GetWidth(); x++)
{
for (int y = 0; y < image.GetHeight(); y++)
{
int iteration = 0;
float x0 = minX + xRange * x / width;
float y0 = maxY - yRange * y / height;
float xx = 0.0f;
float yy = 0.0f;
float x2 = 0.0f;
float y2 = 0.0f;
while (x2 + y2 <= 4 && iteration < maxIteration)
{
yy = 2 * xx * yy + y0;
xx = x2 - y2 + x0;
x2 = xx * xx;
y2 = yy * yy;
iteration += 1;
}
float m = (float)iteration / (float)maxIteration;
Color color = HSV(360.0f * m, 1.0f, Mathf.Ceil(1.0f - 1.1f * m));
image.SetPixel(x, y, color);
}
}
}

public void BenchmarkMandelbrotSet()
{
mandelbrot_set(WIDTH, HEIGHT, MAX_ITERATION);
}
}
112 changes: 112 additions & 0 deletions benchmarks/csharp/MerkleTrees.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using Godot;

public partial class MerkleTrees : Benchmark
{
// Based on https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/merkletrees/1.cs
class TreeNode
{
public long? value;
public long? hash;
public TreeNode left;
public TreeNode right;

public TreeNode(long? value, TreeNode left = null, TreeNode right = null)
{
this.value = value;
this.left = left;
this.right = right;
}

public static TreeNode Create(int d)
{
return d == 0 ? new TreeNode(1L, null, null)
: new TreeNode(null, Create(d - 1), Create(d - 1));
}

public bool Check()
{
if (hash != null)
{
if (value != null)
{
return true;
}
else if (left != null && right != null)
{
return left.Check() && right.Check();
}
}
return false;
}

public long GetHash()
{
if (hash.Value is long v)
{
return v;
}
return default;
}

public void CalHash()
{
if (hash == null)
{
if (value.HasValue)
{
hash = value;
}
else if (left != null && right != null)
{
left.CalHash();
right.CalHash();
hash = left.GetHash() + right.GetHash();
}
}
}
}

const int MinDepth = 4;
public static void CalculateMerkleTrees(int input)
{
int maxDepth = Mathf.Max(MinDepth + 2, input);

int stretchDepth = maxDepth + 1;
TreeNode stretchTree = TreeNode.Create(stretchDepth);
stretchTree.CalHash();
GD.Print($"stretch tree of depth {stretchDepth}\t root hash: {stretchTree.GetHash()} check: {stretchTree.Check().ToString().ToLowerInvariant()}");

TreeNode longLivedTree = TreeNode.Create(maxDepth);
int maxPlusMinDepth = maxDepth + MinDepth;
for (int depth = MinDepth; depth < maxDepth; depth += 2)
{
int iterations = 1 << (maxPlusMinDepth - depth);
long sum = 0;
for (int i = 0; i < iterations; i++)
{
TreeNode tree = TreeNode.Create(depth);
tree.CalHash();
sum += tree.GetHash();
}
GD.Print($"{iterations}\t trees of depth {depth}\t root hash sum: {sum}");
}

longLivedTree.CalHash();
GD.Print($"long lived tree of depth {maxDepth}\t root hash: {longLivedTree.GetHash()} check: {longLivedTree.Check().ToString().ToLowerInvariant()}");
}

public void BenchmarkMerkleTrees13()
{
CalculateMerkleTrees(13);
}

public void BenchmarkMerkleTrees15()
{
CalculateMerkleTrees(15);
}

public void BenchmarkMerkleTrees18()
{
CalculateMerkleTrees(18);
}
}
Loading

0 comments on commit bfd8458

Please sign in to comment.