142 lines
4.8 KiB
C#
142 lines
4.8 KiB
C#
|
|
using System;
|
||
|
|
using System.Diagnostics;
|
||
|
|
using Google.Protobuf;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
public class BenchmarkRunner : MonoBehaviour
|
||
|
|
{
|
||
|
|
private const int N = 10_000;
|
||
|
|
private const int Warmup = 1_000;
|
||
|
|
|
||
|
|
private unsafe void Start()
|
||
|
|
{
|
||
|
|
var apMsg = new Arpack.Messages.MoveMessage
|
||
|
|
{
|
||
|
|
Position = new Arpack.Messages.Vector3 { X = 100, Y = -50, Z = 0 },
|
||
|
|
Velocity = new float[] { 1.5f, -2.5f, 0f },
|
||
|
|
Waypoints = new Arpack.Messages.Vector3[]
|
||
|
|
{
|
||
|
|
new Arpack.Messages.Vector3 { X = 10, Y = 20, Z = 0 },
|
||
|
|
new Arpack.Messages.Vector3 { X = -10, Y = 0, Z = 100 },
|
||
|
|
},
|
||
|
|
PlayerID = 999,
|
||
|
|
Active = true,
|
||
|
|
Visible = false,
|
||
|
|
Ghost = true,
|
||
|
|
Name = "PlayerOne",
|
||
|
|
};
|
||
|
|
|
||
|
|
var pbMsg = new Benchproto.MoveMessage
|
||
|
|
{
|
||
|
|
Position = new Benchproto.Vector3 { X = 100, Y = -50, Z = 0 },
|
||
|
|
PlayerId = 999,
|
||
|
|
Active = true,
|
||
|
|
Visible = false,
|
||
|
|
Ghost = true,
|
||
|
|
Name = "PlayerOne",
|
||
|
|
};
|
||
|
|
pbMsg.Velocity.AddRange(new float[] { 1.5f, -2.5f, 0f });
|
||
|
|
pbMsg.Waypoints.Add(new Benchproto.Vector3 { X = 10, Y = 20, Z = 0 });
|
||
|
|
pbMsg.Waypoints.Add(new Benchproto.Vector3 { X = -10, Y = 0, Z = 100 });
|
||
|
|
|
||
|
|
byte[] apBuf = new byte[256];
|
||
|
|
int apWireSize;
|
||
|
|
fixed (byte* ptr = apBuf) { apWireSize = apMsg.Serialize(ptr); }
|
||
|
|
|
||
|
|
byte[] apBytes = new byte[apWireSize];
|
||
|
|
Array.Copy(apBuf, apBytes, apWireSize);
|
||
|
|
|
||
|
|
byte[] pbBytes = pbMsg.ToByteArray();
|
||
|
|
int pbWireSize = pbBytes.Length;
|
||
|
|
byte[] protoOutputBuf = new byte[256];
|
||
|
|
|
||
|
|
// Warmup (JIT)
|
||
|
|
for (int i = 0; i < Warmup; i++)
|
||
|
|
{
|
||
|
|
fixed (byte* ptr = apBuf) { apMsg.Serialize(ptr); }
|
||
|
|
fixed (byte* ptr = apBytes) { Arpack.Messages.MoveMessage.Deserialize(ptr, out _); }
|
||
|
|
_ = pbMsg.ToByteArray();
|
||
|
|
_ = Benchproto.MoveMessage.Parser.ParseFrom(pbBytes);
|
||
|
|
var cos = new CodedOutputStream(protoOutputBuf);
|
||
|
|
pbMsg.WriteTo(cos);
|
||
|
|
cos.Flush();
|
||
|
|
}
|
||
|
|
|
||
|
|
Stopwatch sw;
|
||
|
|
long gcBefore, gcAfter;
|
||
|
|
|
||
|
|
// ArPack Serialize
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
gcBefore = GC.GetTotalMemory(false);
|
||
|
|
sw = Stopwatch.StartNew();
|
||
|
|
for (int i = 0; i < N; i++)
|
||
|
|
{
|
||
|
|
fixed (byte* ptr = apBuf) { apMsg.Serialize(ptr); }
|
||
|
|
}
|
||
|
|
sw.Stop();
|
||
|
|
gcAfter = GC.GetTotalMemory(false);
|
||
|
|
Log("ArPack Serialize ", sw, N, gcAfter - gcBefore);
|
||
|
|
|
||
|
|
// ArPack Deserialize
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
gcBefore = GC.GetTotalMemory(false);
|
||
|
|
sw = Stopwatch.StartNew();
|
||
|
|
for (int i = 0; i < N; i++)
|
||
|
|
{
|
||
|
|
fixed (byte* ptr = apBytes) { Arpack.Messages.MoveMessage.Deserialize(ptr, out _); }
|
||
|
|
}
|
||
|
|
sw.Stop();
|
||
|
|
gcAfter = GC.GetTotalMemory(false);
|
||
|
|
Log("ArPack Deserialize ", sw, N, gcAfter - gcBefore);
|
||
|
|
|
||
|
|
// Proto Serialize (alloc)
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
gcBefore = GC.GetTotalMemory(false);
|
||
|
|
sw = Stopwatch.StartNew();
|
||
|
|
byte[] pbOut = null;
|
||
|
|
for (int i = 0; i < N; i++)
|
||
|
|
{
|
||
|
|
pbOut = pbMsg.ToByteArray();
|
||
|
|
}
|
||
|
|
sw.Stop();
|
||
|
|
gcAfter = GC.GetTotalMemory(false);
|
||
|
|
Log("Proto Serialize (alloc) ", sw, N, gcAfter - gcBefore);
|
||
|
|
_ = pbOut;
|
||
|
|
|
||
|
|
// Proto Deserialize (alloc)
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
gcBefore = GC.GetTotalMemory(false);
|
||
|
|
sw = Stopwatch.StartNew();
|
||
|
|
for (int i = 0; i < N; i++)
|
||
|
|
{
|
||
|
|
_ = Benchproto.MoveMessage.Parser.ParseFrom(pbBytes);
|
||
|
|
}
|
||
|
|
sw.Stop();
|
||
|
|
gcAfter = GC.GetTotalMemory(false);
|
||
|
|
Log("Proto Deserialize (alloc)", sw, N, gcAfter - gcBefore);
|
||
|
|
|
||
|
|
// Proto Serialize (reuse buffer)
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
gcBefore = GC.GetTotalMemory(false);
|
||
|
|
sw = Stopwatch.StartNew();
|
||
|
|
for (int i = 0; i < N; i++)
|
||
|
|
{
|
||
|
|
var cos = new CodedOutputStream(protoOutputBuf);
|
||
|
|
pbMsg.WriteTo(cos);
|
||
|
|
cos.Flush();
|
||
|
|
}
|
||
|
|
sw.Stop();
|
||
|
|
gcAfter = GC.GetTotalMemory(false);
|
||
|
|
Log("Proto Serialize (reuse) ", sw, N, gcAfter - gcBefore);
|
||
|
|
|
||
|
|
UnityEngine.Debug.Log($"[Bench] Wire sizes — ArPack: {apWireSize} bytes | Protobuf: {pbWireSize} bytes");
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void Log(string label, Stopwatch sw, int n, long gcDelta)
|
||
|
|
{
|
||
|
|
double nsPerOp = sw.Elapsed.TotalMilliseconds * 1_000_000.0 / n;
|
||
|
|
long bPerOp = Math.Max(0, gcDelta) / n;
|
||
|
|
UnityEngine.Debug.Log($"[Bench] {label}: {nsPerOp,8:F1} ns/op | {bPerOp,6} B/op");
|
||
|
|
}
|
||
|
|
}
|