Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize engine construction #1621

Merged
merged 1 commit into from
Aug 25, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 17 additions & 5 deletions Jint.Benchmark/EngineConstructionBenchmark.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
using BenchmarkDotNet.Attributes;
using Esprima;
using Esprima.Ast;
using Jint.Native;

namespace Jint.Benchmark;

[MemoryDiagnoser]
public class EngineConstructionBenchmark
{
private readonly Script _program;
private Script _program;
private Script _simple;

public EngineConstructionBenchmark()
[GlobalSetup]
public void GlobalSetup()
{
var parser = new JavaScriptParser();
_program = parser.ParseScript("return [].length + ''.length");
_program = parser.ParseScript("([].length + ''.length)");
_simple = parser.ParseScript("1");
new Engine().Evaluate(_program);
}

[Benchmark]
public double BuildEngine()
public Engine BuildEngine()
{
var engine = new Engine();
return engine.Evaluate(_program).AsNumber();
return engine;
}

[Benchmark]
public JsValue EvaluateSimple()
{
var engine = new Engine();
return engine.Evaluate(_simple);
}
}
6 changes: 6 additions & 0 deletions Jint/Collections/HybridDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public HybridDictionary(int initialSize, bool checkExistingKeys)
}
}

protected HybridDictionary(StringDictionarySlim<TValue> dictionary)
{
_checkExistingKeys = true;
_dictionary = dictionary;
}

public TValue this[Key key]
{
get
Expand Down
4 changes: 4 additions & 0 deletions Jint/Collections/PropertyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ public PropertyDictionary()
public PropertyDictionary(int capacity, bool checkExistingKeys) : base(capacity, checkExistingKeys)
{
}

public PropertyDictionary(StringDictionarySlim<PropertyDescriptor> properties) : base(properties)
{
}
}
}
9 changes: 9 additions & 0 deletions Jint/Collections/StringDictionarySlim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ public void SetOrUpdateValue<TState>(Key key, Func<TValue, TState, TValue> updat
return ref AddKey(key, bucketIndex);
}

/// <summary>
/// Adds a new item and expects key to not to exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddDangerous(in Key key, TValue value)
{
AddKey(key, key.HashCode & (_buckets.Length - 1)) = value;
}

public ref TValue this[Key key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
27 changes: 18 additions & 9 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ namespace Jint
/// </summary>
public sealed partial class Engine : IDisposable
{
private static readonly Options _defaultEngineOptions = new();

private readonly ParserOptions _defaultParserOptions;
private readonly JavaScriptParser _defaultParser;

internal readonly ExecutionContextStack _executionContexts;
private readonly ExecutionContextStack _executionContexts;
private JsValue _completionValue = JsValue.Undefined;
internal EvaluationContext? _activeEvaluationContext;

private readonly EventLoop _eventLoop = new();

private readonly Agent _agent = new Agent();
private readonly Agent _agent = new();

// lazy properties
private DebugHandler? _debugHandler;
Expand All @@ -51,7 +53,7 @@ public sealed partial class Engine : IDisposable
internal readonly JsValueArrayPool _jsValueArrayPool;
internal readonly ExtensionMethodCache _extensionMethods;

public ITypeConverter ClrTypeConverter { get; internal set; } = null!;
public ITypeConverter ClrTypeConverter { get; internal set; }

// cache of types used when resolving CLR type names
internal readonly Dictionary<string, Type?> TypeCache = new();
Expand All @@ -73,37 +75,44 @@ public sealed partial class Engine : IDisposable
/// <summary>
/// Constructs a new engine instance.
/// </summary>
public Engine() : this((Action<Options>?) null)
public Engine() : this(null, null)
{
}

/// <summary>
/// Constructs a new engine instance and allows customizing options.
/// </summary>
public Engine(Action<Options>? options)
: this((engine, opts) => options?.Invoke(opts))
: this(null, options != null ? (_, opts) => options.Invoke(opts) : null)
{
}

/// <summary>
/// Constructs a new engine with a custom <see cref="Options"/> instance.
/// </summary>
public Engine(Options options) : this((e, o) => e.Options = options)
public Engine(Options options) : this(options, null)
{
}

/// <summary>
/// Constructs a new engine instance and allows customizing options.
/// </summary>
/// <remarks>The provided engine instance in callback is not guaranteed to be fully configured</remarks>
public Engine(Action<Engine, Options> options)
public Engine(Action<Engine, Options> options) : this(null, options)
{
}

private Engine(Options? options, Action<Engine, Options>? configure)
{
Advanced = new AdvancedOperations(this);
ClrTypeConverter = new DefaultTypeConverter(this);

_executionContexts = new ExecutionContextStack(2);

Options = new Options();
options?.Invoke(this, Options);
// we can use default options if there's no action to modify it
Options = options ?? (configure is not null ? new Options() : _defaultEngineOptions);

configure?.Invoke(this, Options);

_extensionMethods = ExtensionMethodCache.Build(Options.Interop.ExtensionMethodTypes);

Expand Down