Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sandbox/Benchmark.NativeAot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bin/
obj/
BenchmarkDotNet.Artifacts/
15 changes: 15 additions & 0 deletions sandbox/Benchmark.NativeAot/Benchmark.NativeAot.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<ProjectReference Include="../../src/Lua/Lua.csproj" />
<Compile Include="../Benchmark/ComparisonWorkloads.cs" Link="ComparisonWorkloads.cs" />
<Compile Include="../Benchmark/LuaCSharpComparison.cs" Link="LuaCSharpComparison.cs" />
<EmbeddedResource Include="../Benchmark/n-body.lua" LogicalName="Comparison.n-body.lua" />
</ItemGroup>
</Project>
34 changes: 34 additions & 0 deletions sandbox/Benchmark.NativeAot/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using BenchmarkDotNet.Running;

if (args.Contains("--verify"))
{
foreach (
var workload in ComparisonWorkloads
.Cases.Concat(ComparisonWorkloads.CompilationCases)
.DistinctBy(c => c.Name)
)
RuntimeVerification.Verify(workload);
return;
}

BenchmarkSwitcher
.FromTypes([typeof(RuntimeExecutionComparison), typeof(RuntimeCompilationComparison)])
.Run(args);

static class RuntimeVerification
{
public static void Verify(ComparisonCase workload)
{
foreach (var bytes in new[] { false, true })
{
using var runner = new LuaCSharpComparison(workload.Source, bytes);
for (var i = 0; i < 2; i++)
ComparisonWorkloads.Check(
workload,
bytes ? "Lua-CSharp bytes" : "Lua-CSharp",
runner.Run()
);
}
Console.WriteLine($"PASS {workload.Name}: {workload.Expected:R}");
}
}
42 changes: 42 additions & 0 deletions sandbox/Benchmark.NativeAot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# .NET 10 JIT and NativeAOT benchmarks

Compare Lua-CSharp source compilation and bytecode execution under .NET 10 JIT
and NativeAOT, using the sources and checksum checks from `../Benchmark`.
NativeAOT compiles the C# interpreter; Lua source is still compiled to Lua
bytecode and interpreted.

Run from this directory:

```powershell
dotnet restore Benchmark.NativeAot.csproj
dotnet build Benchmark.NativeAot.csproj -c Release --no-restore
dotnet bin/Release/net10.0/Benchmark.NativeAot.dll --verify
dotnet bin/Release/net10.0/Benchmark.NativeAot.dll --filter '*RuntimeExecutionComparison*numeric-loop*' --exporters json
dotnet bin/Release/net10.0/Benchmark.NativeAot.dll --filter '*RuntimeCompilationComparison*numeric-loop*' --exporters json
```

Use a case filter before selecting a whole suite. `--list flat` lists the
benchmarks; no arguments opens BenchmarkDotNet's selector. `--artifacts <path>`
keeps outputs separate and `--keepFiles` retains generated projects/executables.

The BenchmarkDotNet NativeAOT toolchain publishes a native benchmark process.
Install the platform's NativeAOT build prerequisites (Visual C++ build tools
and a Windows SDK on Windows). The native job pins compiler/runtime **10.0.14**,
uses speed optimization and the current CPU's native instruction set. Record
the resolved JIT runtime version too. These native executables target the build
machine's CPU capabilities.

`--verify` checks all 15 distinct workloads in the managed host, twice in each
string mode. Each benchmark's setup also runs its checksum checks inside the
actual JIT or NativeAOT benchmark process before timing. Timed execution uses
UTF-16 mode with ASCII inputs, retained state and a compiled chunk. Compilation
has six source cases, a fresh state outside each batch of 64 loads, and no Lua
execution during measurement. Both jobs use three launches. Compilation warms
for two seconds, then measures 20 batches after 20 warmups; execution measures
10 iterations after five warmups with a 250 ms target iteration time.

Run timing jobs serially. JIT uses runtime defaults; NativeAOT has no tiered JIT
or dynamic PGO. Compare the same sources and keep these profiles distinct from
cross-engine results. MemoryDiagnoser reports managed allocation; generated
executable sizes also include BenchmarkDotNet and metadata, so neither is a
minimal embedding application size or total native-Lua memory measurement.
140 changes: 140 additions & 0 deletions sandbox/Benchmark.NativeAot/RuntimeComparison.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.NativeAot;
using Lua;
using Lua.Runtime;
using Lua.Standard;
using Perfolizer.Horology;

static class RuntimeJobs
{
public static Job NativeAot(Job job) =>
job.WithRuntime(NativeAotRuntime.Net10_0)
.WithArguments([
new MsBuildArgument("/p:RestoreSources=https://api.nuget.org/v3/index.json"),
])
.WithToolchain(
NativeAotToolchain
.CreateBuilder()
.UseNuGet("10.0.14", "https://api.nuget.org/v3/index.json")
.IlcInstructionSet("native")
.TargetFrameworkMoniker("net10.0")
.RuntimeFrameworkVersion("10.0.14")
.UseNuGetClearTag(true)
.DisplayName("NativeAOT 10.0.14")
.ToToolchain()
)
.WithId("NET10-NativeAOT");
}

public sealed class RuntimeExecutionConfig : ManualConfig
{
public RuntimeExecutionConfig()
{
AddDiagnoser(MemoryDiagnoser.Default);
var job = Job
.ShortRun.WithLaunchCount(3)
.WithWarmupCount(5)
.WithIterationCount(10)
.WithIterationTime(TimeInterval.FromMilliseconds(250));
AddJob(job.WithRuntime(CoreRuntime.Core10_0).WithId("NET10-JIT").AsBaseline());
AddJob(RuntimeJobs.NativeAot(job));
}
}

public sealed class RuntimeCompilationConfig : ManualConfig
{
public RuntimeCompilationConfig()
{
AddDiagnoser(MemoryDiagnoser.Default);
var job = Job
.ShortRun.WithLaunchCount(3)
.WithWarmupCount(20)
.WithIterationCount(20)
.WithInvocationCount(1)
.WithUnrollFactor(1);
AddJob(job.WithRuntime(CoreRuntime.Core10_0).WithId("NET10-JIT").AsBaseline());
AddJob(RuntimeJobs.NativeAot(job));
}
}

[Config(typeof(RuntimeExecutionConfig))]
[BenchmarkCategory("Execution")]
public class RuntimeExecutionComparison
{
[ParamsSource(nameof(Cases))]
public string Case { get; set; } = "numeric-loop";
public static IEnumerable<string> Cases => ComparisonWorkloads.Cases.Select(c => c.Name);
LuaCSharpComparison runner = default!;

[GlobalSetup]
public void Setup()
{
var workload = ComparisonWorkloads.Find(Case);
RuntimeVerification.Verify(workload);
runner = new(workload.Source);
}

[Benchmark]
public double Execute() => runner.Run();

[GlobalCleanup]
public void Cleanup() => runner.Dispose();
}

[Config(typeof(RuntimeCompilationConfig))]
[BenchmarkCategory("Compilation")]
public class RuntimeCompilationComparison
{
[ParamsSource(nameof(Cases))]
public string Case { get; set; } = "numeric-loop";
public static IEnumerable<string> Cases =>
ComparisonWorkloads.CompilationCases.Select(c => c.Name);
string source = default!;
LuaState state = default!;
const int BatchSize = 64;

[GlobalSetup]
public void Setup()
{
var workload = ComparisonWorkloads.FindCompilation(Case);
RuntimeVerification.Verify(workload);
source = workload.Source;
var timer = Stopwatch.StartNew();
do
{
ResetState();
try
{
Compile();
}
finally
{
Cleanup();
}
} while (timer.ElapsedMilliseconds < 2000);
}

[IterationSetup]
public void ResetState()
{
state = LuaState.Create();
state.OpenStandardLibraries();
}

[Benchmark(OperationsPerInvoke = BatchSize)]
public LuaClosure Compile()
{
LuaClosure result = default!;
for (var i = 0; i < BatchSize; i++)
result = state.Load(source, "comparison");
return result;
}

[IterationCleanup]
public void Cleanup() => state.Dispose();
}
6 changes: 6 additions & 0 deletions sandbox/Benchmark.NativeAot/global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
3 changes: 3 additions & 0 deletions sandbox/Benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,6 @@ single overall ranking without application-specific workload weights.

The legacy suites include file I/O, source execution and focused API tests with
different boundaries. Their results should not be mixed with this suite.

For Lua-CSharp JIT versus NativeAOT compilation and execution, use the
[NativeAOT benchmarks](../Benchmark.NativeAot/README.md), which share these sources.
Loading