Skip to content

Commit

Permalink
Optimize engine construction (#1621)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Aug 25, 2023
1 parent f4982ee commit 7fd4893
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 129 deletions.
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

0 comments on commit 7fd4893

Please sign in to comment.