Permalink
Browse files

Optimize interop (#519)

  • Loading branch information...
lahma authored and sebastienros committed Jul 11, 2018
1 parent f9e65d3 commit 8bbd3904865d68765fb97b9bcba972a8eaf5d95c
@@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using Jint.Native;
using Jint.Native.Array;
namespace Jint.Benchmark
{
[MemoryDiagnoser]
public class InteropBenchmark
{
private const int OperationsPerInvoke = 1_000;
public class Person
{
public string Name { get; set; }
}
private Engine _engine;
[GlobalSetup]
public void Setup()
{
_engine = new Engine(cfg => cfg.AllowClr(
typeof(Person).GetTypeInfo().Assembly,
typeof(Console).GetTypeInfo().Assembly,
typeof(System.IO.File).GetTypeInfo().Assembly))
.SetValue("log", new Action<object>(Console.WriteLine))
.SetValue("assert", new Action<bool>(x => { }))
.SetValue("equal", new Action<object, object>((x, y) => { }));
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void DelegatesCanBeSet()
{
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("square", new Func<double, double>(x => x * x));
_engine.Execute("assert(square(10) === 100);");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void ExtraParametersAreIgnored()
{
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("passNumber", new Func<int, int>(x => x));
_engine.Execute("assert(passNumber(123,'test',{},[],null) === 123);");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void GetObjectProperties()
{
var p = new Person
{
Name = "Mickey Mouse"
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute("assert(p.Name === 'Mickey Mouse');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void InvokeObjectMethods()
{
var p = new Person
{
Name = "Mickey Mouse"
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute(@"assert(p.ToString() === 'Mickey Mouse');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void SetObjectProperties()
{
var p = new Person
{
Name = "Mickey Mouse"
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute("p.Name = 'Donald Duck'; assert(p.Name === 'Donald Duck');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void GetIndexUsingStringKey()
{
var dictionary = new Dictionary<string, Person>();
dictionary.Add("person1", new Person {Name = "Mickey Mouse"});
dictionary.Add("person2", new Person {Name = "Goofy"});
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("dictionary", dictionary);
_engine.Execute("assert(dictionary['person1'].Name === 'Mickey Mouse'); assert(dictionary['person2'].Name === 'Goofy');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void GenericMethods()
{
var dictionary = new Dictionary<int, string>
{
{1, "Mickey Mouse"}
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("dictionary", dictionary);
_engine.Execute("dictionary.Clear(); dictionary.Add(2, 'Goofy'); assert(dictionary[2] === 'Goofy');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void MultiGenericTypes()
{
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.Execute(@"
var type = System.Collections.Generic.Dictionary(System.Int32, System.String);
var dictionary = new type();
dictionary.Add(1, 'Mickey Mouse');
dictionary.Add(2, 'Goofy');
assert(dictionary[2] === 'Goofy');
");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void IndexOnList()
{
var list = new List<object>(2);
list.Add("Mickey Mouse");
list.Add("Goofy");
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("list", list);
_engine.Execute("list[1] = 'Donald Duck'; assert(list[1] === 'Donald Duck');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void EcmaValuesAreAutomaticallyConvertedWhenSetInPoco()
{
var p = new Person
{
Name = "foo",
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute(@"
assert(p.Name === 'foo');
assert(p.Age === 0);
p.Name = 'bar';
p.Age = 10;
");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Trim()
{
var p = new Person
{
Name = "Mickey Mouse "
};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute(@"
assert(p.Name === 'Mickey Mouse ');
p.Name = p.Name.trim();
assert(p.Name === 'Mickey Mouse');
");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void MathFloor()
{
var p = new Person();
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("p", p);
_engine.Execute("p.Age = Math.floor(1.6); assert(p.Age === 1);");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void DelegateAsFunction()
{
var even = new Func<int, bool>(x => x % 2 == 0);
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("even", even);
_engine.Execute("assert(even(2) === true);");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void ConvertArrayToArrayInstance()
{
var ints = new[] {1, 2, 3, 4, 5, 6};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine
.SetValue("values", ints)
.Execute("values.filter(function(x){ return x % 2 == 0; })");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void ConvertListsToArrayInstance()
{
var ints = new List<object> {1, 2, 3, 4, 5, 6};
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine
.SetValue("values", ints)
.Execute("new Array(values).filter(function(x){ return x % 2 == 0; })");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void ConvertArrayInstanceToArray()
{
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.Execute("'foo@bar.com'.split('@');");
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void LoopWithNativeEnumerator()
{
JsValue Adder(JsValue argValue)
{
ArrayInstance args = argValue.AsArray();
double sum = 0;
foreach (var item in args)
{
if (item.IsNumber())
{
sum += item.AsNumber();
}
}
return sum;
}
for (int i = 0; i < OperationsPerInvoke; ++i)
{
_engine.SetValue("getSum", new Func<JsValue, JsValue>(Adder));
_engine.Execute("getSum([1,2,3]);");
}
}
}
}
View
@@ -83,6 +83,9 @@ public sealed class Engine
{ typeof(System.Text.RegularExpressions.Regex), (Engine engine, object v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
};
internal readonly Dictionary<(Type, string), Func<Engine, object, PropertyDescriptor>> ClrPropertyDescriptorFactories =
new Dictionary<(Type, string), Func<Engine, object, PropertyDescriptor>>();
internal JintCallStack CallStack = new JintCallStack();
static Engine()
@@ -23,7 +23,7 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments)
ExceptionHelper.ThrowTypeError(Engine);
});
return f.Call(BoundThis, BoundArgs.Union(arguments).ToArray());
return f.Call(BoundThis, CreateArguments(arguments));
}
public ObjectInstance Construct(JsValue[] arguments)
@@ -33,7 +33,7 @@ public ObjectInstance Construct(JsValue[] arguments)
ExceptionHelper.ThrowTypeError(Engine);
});
return target.Construct(BoundArgs.Union(arguments).ToArray());
return target.Construct(CreateArguments(arguments));
}
public override bool HasInstance(JsValue v)
@@ -45,5 +45,10 @@ public override bool HasInstance(JsValue v)
return f.HasInstance(v);
}
private JsValue[] CreateArguments(JsValue[] arguments)
{
return Enumerable.Union(BoundArgs, arguments).ToArray();
}
}
}
View
@@ -280,7 +280,7 @@ public static JsValue FromObject(Engine engine, object value)
return new DelegateWrapper(engine, d);
}
if (value.GetType().IsEnum())
if (value.GetType().IsEnum)
{
return JsNumber.Create((int) value);
}
@@ -38,7 +38,7 @@ namespace Jint.Native.Number.Dtoa
// have the most significant bit of the significand set.
// Multiplication and Subtraction do not normalize their results.
// DiyFp are not designed to contain special doubles (NaN and Infinity).
internal struct DiyFp
internal readonly struct DiyFp
{
internal const int KSignificandSize = 64;
private const ulong KUint64MSB = 0x8000000000000000L;
@@ -49,8 +49,8 @@ internal DiyFp(long f, int e)
E = e;
}
public long F { get; }
public int E { get; }
public readonly long F;
public readonly int E;
private static bool Uint64Gte(long a, long b)
{
@@ -61,7 +61,7 @@ private static bool Uint64Gte(long a, long b)
// Returns a - b.
// The exponents of both numbers must be the same and this must be bigger
// than other. The result will not be normalized.
internal static DiyFp Minus(DiyFp a, DiyFp b)
internal static DiyFp Minus(in DiyFp a, in DiyFp b)
{
Debug.Assert(a.E == b.E);
Debug.Assert(Uint64Gte(a.F, b.F));
@@ -72,7 +72,7 @@ internal static DiyFp Minus(DiyFp a, DiyFp b)
// this = this * other.
// returns a * b;
internal static DiyFp Times(DiyFp a, DiyFp b)
internal static DiyFp Times(in DiyFp a, in DiyFp b)
{
DiyFp result = new DiyFp(a.F, a.E);
// Simply "emulates" a 128 bit multiplication.
@@ -349,9 +349,10 @@ internal class FastDtoa
// represent 'w' we can stop. Everything inside the interval low - high
// represents w. However we have to pay attention to low, high and w's
// imprecision.
private static bool DigitGen(DiyFp low,
DiyFp w,
DiyFp high,
private static bool DigitGen(
in DiyFp low,
in DiyFp w,
in DiyFp high,
FastDtoaBuilder buffer,
int mk)
{
Oops, something went wrong.

0 comments on commit 8bbd390

Please sign in to comment.