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
74 changes: 74 additions & 0 deletions Jint.Benchmark/CpuProfileDriver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#nullable enable
using System.Globalization;

namespace Jint.Benchmark;

/// <summary>
/// Runs a single engine-comparison script in a tight loop so an external sampling profiler
/// (dotnet-trace collect -- ...) can attribute CPU time. Mirrors EngineComparisonBenchmark's
/// per-op shape: fresh engine per iteration, strict mode, dromaeo helper stubs.
/// Also prints wall-clock per iteration, usable as a quick source-vs-prepared A/B timer.
/// Not a benchmark; not registered with BDN. Temporary diagnostic.
/// </summary>
internal static class CpuProfileDriver
{
private const string DromaeoHelpers = """
var startTest = function () { };
var test = function (name, fn) { fn(); };
var endTest = function () { };
var prep = function (fn) { fn(); };
""";

public static int Run(string[] args)
{
// args[0] = "--profile-cpu", args[1] = script name, args[2] = iterations, args[3] = "source" | "prepared"
var scriptName = args.Length > 1 ? args[1] : "stopwatch";
var iterations = args.Length > 2 ? int.Parse(args[2], CultureInfo.InvariantCulture) : 20;
var variant = args.Length > 3 ? args[3] : "source";

var path = Path.Combine(AppContext.BaseDirectory, "Scripts", scriptName + ".js");
if (!File.Exists(path))
{
Console.Error.WriteLine($"Script not found: {path}");
return 2;
}

var src = File.ReadAllText(path);
if (scriptName.Contains("dromaeo", StringComparison.Ordinal))
{
src = DromaeoHelpers + Environment.NewLine + Environment.NewLine + src;
}

var prepared = Engine.PrepareScript(src, strict: true);
var usePrepared = string.Equals(variant, "prepared", StringComparison.Ordinal);

// Warm-up to settle JIT and statics before the profiler-relevant loop dominates samples.
for (var i = 0; i < 2; i++)
{
RunOnce();
}

var sw = System.Diagnostics.Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
RunOnce();
}
sw.Stop();

Console.WriteLine($"{scriptName} [{variant}]: {iterations} iterations in {sw.Elapsed.TotalMilliseconds:F1} ms ({sw.Elapsed.TotalMilliseconds / iterations:F3} ms/iter)");
return 0;

void RunOnce()
{
var engine = new Engine(static options => options.Strict());
if (usePrepared)
{
engine.Execute(prepared);
}
else
{
engine.Execute(src);
}
}
}
}
51 changes: 51 additions & 0 deletions Jint.Benchmark/DateConstructionBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using BenchmarkDotNet.Attributes;
using Jint.Native;

namespace Jint.Benchmark;

/// <summary>
/// Isolates `new Date()` current-time construction (the stopwatch.js hot allocation) and
/// guards the explicit-milliseconds constructor which must keep full TimeClip semantics.
/// </summary>
[MemoryDiagnoser]
[HideColumns("Error", "Gen0", "Gen1", "Gen2")]
public class DateConstructionBenchmarks
{
private Engine _engine = null!;
private Prepared<Script> _newDateNow;
private Prepared<Script> _newDateMillis;

[GlobalSetup]
public void Setup()
{
_newDateNow = Engine.PrepareScript("""
(function() {
var last = null;
for (var i = 0; i < 100000; i++) {
last = new Date();
}
return last;
})();
""", strict: true);

_newDateMillis = Engine.PrepareScript("""
(function() {
var last = null;
for (var i = 0; i < 100000; i++) {
last = new Date(1717774870000 + i);
}
return last;
})();
""", strict: true);

_engine = new Engine(static options => options.Strict());
_engine.Evaluate(_newDateNow);
_engine.Evaluate(_newDateMillis);
}

[Benchmark]
public JsValue NewDateNow() => _engine.Evaluate(_newDateNow);

[Benchmark]
public JsValue NewDateMillis() => _engine.Evaluate(_newDateMillis);
}
5 changes: 5 additions & 0 deletions Jint.Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
return MemoryProbe.Run(args);
}

if (args.Length > 0 && args[0] == "--profile-cpu")
{
return CpuProfileDriver.Run(args);
}

BenchmarkSwitcher
.FromAssembly(typeof(ArrayBenchmark).GetTypeInfo().Assembly)
.Run(args);
Expand Down
Loading
Loading