瀏覽代碼

Add fast paths for object Get/Set for simple objects (#2050)

Marko Lahma 5 月之前
父節點
當前提交
5342ebb3eb
共有 2 個文件被更改,包括 73 次插入10 次删除
  1. 54 0
      Jint.Benchmark/ClassBenchmark.cs
  2. 19 10
      Jint/Native/Object/ObjectInstance.cs

+ 54 - 0
Jint.Benchmark/ClassBenchmark.cs

@@ -0,0 +1,54 @@
+using BenchmarkDotNet.Attributes;
+
+namespace Jint.Benchmark;
+
+[MemoryDiagnoser]
+public class ClassBenchmark
+{
+    private Engine _engine;
+
+    [IterationSetup]
+    public void Setup()
+    {
+        _engine = new Engine();
+        _engine.Execute("""
+                        class A { x = 1; }; 
+                        class B extends A { y = 2; };
+                        class C extends B { z = 3; }; 
+                        class D extends C { x2 = 1; };
+                        class E extends D { x3 = 1; };
+                        class F extends E { x4 = 1; }
+                        """);
+        _engine.Execute("const target = new F();");
+    }
+
+    [Benchmark]
+    public void ConstructSimple()
+    {
+        var script = Engine.PrepareScript("new A();");
+        for (var i = 0; i < 400_000; ++i)
+        {
+            _engine.Evaluate(script);
+        }
+    }
+
+    [Benchmark]
+    public void ConstructDeepInheritance()
+    {
+        var script = Engine.PrepareScript("new F();");
+        for (var i = 0; i < 80_000; ++i)
+        {
+            _engine.Evaluate(script);
+        }
+    }
+
+    [Benchmark]
+    public void GetSet()
+    {
+        var script = Engine.PrepareScript("target.x4 = 42; target.x4;");
+        for (var i = 0; i < 500_000; ++i)
+        {
+            _engine.Evaluate(script);
+        }
+    }
+}

+ 19 - 10
Jint/Native/Object/ObjectInstance.cs

@@ -354,21 +354,22 @@ public partial class ObjectInstance : JsValue, IEquatable<ObjectInstance>
 
 
     public override JsValue Get(JsValue property, JsValue receiver)
     public override JsValue Get(JsValue property, JsValue receiver)
     {
     {
-        if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property is JsString jsString)
+        if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property.IsString())
         {
         {
             EnsureInitialized();
             EnsureInitialized();
-            if (_properties?.TryGetValue(jsString.ToString(), out var ownDesc) == true)
+            if (_properties?.TryGetValue(property.ToString(), out var ownDesc) == true)
             {
             {
                 return UnwrapJsValue(ownDesc, receiver);
                 return UnwrapJsValue(ownDesc, receiver);
             }
             }
+
+            return Prototype?.Get(property, receiver) ?? Undefined;
         }
         }
-        else
+
+        // slow path
+        var desc = GetOwnProperty(property);
+        if (desc != PropertyDescriptor.Undefined)
         {
         {
-            var desc = GetOwnProperty(property);
-            if (desc != PropertyDescriptor.Undefined)
-            {
-                return UnwrapJsValue(desc, receiver);
-            }
+            return UnwrapJsValue(desc, receiver);
         }
         }
 
 
         return Prototype?.Get(property, receiver) ?? Undefined;
         return Prototype?.Get(property, receiver) ?? Undefined;
@@ -506,9 +507,9 @@ public partial class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     /// </summary>
     /// </summary>
     public override bool Set(JsValue property, JsValue value, JsValue receiver)
     public override bool Set(JsValue property, JsValue value, JsValue receiver)
     {
     {
-        if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property is JsString jsString)
+        if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property.IsString())
         {
         {
-            var key = (Key) jsString.ToString();
+            var key = (Key) property.ToString();
             if (_properties?.TryGetValue(key, out var ownDesc) == true)
             if (_properties?.TryGetValue(key, out var ownDesc) == true)
             {
             {
                 if ((ownDesc._flags & PropertyFlag.Writable) != PropertyFlag.None)
                 if ((ownDesc._flags & PropertyFlag.Writable) != PropertyFlag.None)
@@ -517,6 +518,14 @@ public partial class ObjectInstance : JsValue, IEquatable<ObjectInstance>
                     return true;
                     return true;
                 }
                 }
             }
             }
+            else
+            {
+                var parent = GetPrototypeOf();
+                if (parent is not null)
+                {
+                    return parent.Set(property, value, receiver);
+                }
+            }
         }
         }
 
 
         return SetUnlikely(property, value, receiver);
         return SetUnlikely(property, value, receiver);