Re: [v8-users] Re: Is "extend" keyword a performance antipattern???
"extends" is not an anti-pattern. Of course monomorphic code is fastest. Polymorphic/megamorphic loads and stores have to do more work (specifically, dynamic dispatch), which is going to take a bit more time. Class hierarchies are one way how developers *can* create polymorphic code; what you have here is an example of that. But that doesn't mean that inheritance itself is a problem, or that polymorphism is a problem. The reason this is different from C++ vtables is because JavaScript isn't C++. JavaScript is a dynamic language, and engines must strike a balance between achieving fast execution when the object hierarchies are not modified much, and still having acceptable performance when developers make use of JavaScript's dynamic capabilities and do modify the object hierarchy at runtime. So engines should not have internal mechanics that are too rigid, and would cause huge performance drops when code does something valid but "unexpected". It's easy to point at one pattern and say "clearly this should be handled better", but when you look at a variety of large codebases, they use so many different patterns that it becomes very unclear what internal object representation model would work best on average. FWIW, the original example is essentially equivalent to simple "traditional" polymorphic patterns like the following, which uses no inheritance and no "extends": function DoIt(obj) { obj.done++; } function Cat() {} function Dog() {} // etc. var c = new Cat(); DoIt(c); // Only one type so far -> monomorphic var d = new Dog(); DoIt(d); // Two types -> polymorphic // etc. On Sun, Sep 24, 2017 at 3:51 PM, Caitlin Potterwrote: > So yes, the load/store for `this.done++` can't be reduced to simple > machine ops when `doIt` is megamorphic, as far as I can tell. (5+ receiver > maps should make the load/stores megamorphic, or at least that's what > https://github.com/v8/v8/blob/9a0d5d9700ee269cbe7abee6d62733 > 163dadac14/src/ic/ic.h#L33-L35 seems to indicate). > > That doesn't necessarily mean LoadIC is going to be slow, I guess it > depends on how often you the receiver map is missing from the cache. Slower > than the inlined monomorphic in-object field load/store, but probably not > "anti-pattern" slow. > > I could be wrong about this, but I think the CallIC for individual calls > to `doIt` can be inlined with different type feedback information for the > load/store, so if that shows up in hot code which is monomorphic or > polymorphic, it might produce better code than your example there. > > That said, I'm half guessing about all of this, so I'll leave the rest of > this thread for more knowledgeable people. > > On Sunday, September 24, 2017 at 12:07:58 PM UTC-4, Bogdan Orlov wrote: >> >> After removing all intermediate %OptimizeFunctionOnNextCall() and >> putting at the end (after usage of all Animal subclasses for enough >> feedback) like this >> >> class Animal { >> constructor(){ >> this.done = 0 >> } >> doIt(){ >> this.done++ >> } >> } >> class Cat extends Animal {} >> class Dog extends Animal {} >> class Dog1 extends Animal {} >> class Dog2 extends Animal {} >> class Dog3 extends Animal {} >> >> function test(){ >> var cat = new Cat(); >> cat.doIt(); >> cat.doIt(); >> cat.doIt(); >> var dog = new Dog(); >> dog.doIt(); >> dog.doIt(); >> dog.doIt(); >> var dog1 = new Dog1(); >> dog1.doIt(); >> dog1.doIt(); >> dog1.doIt(); >> var dog2 = new Dog2(); >> dog2.doIt(); >> dog2.doIt(); >> dog2.doIt(); >> var dog3 = new Dog3(); >> dog3.doIt(); >> dog3.doIt(); >> dog3.doIt(); >> %OptimizeFunctionOnNextCall(dog3.doIt) >> dog3.doIt() >> } >> test() >> >> I got this output: >> >> 0x2df967b05180 0 55 push rbp >> 0x2df967b05181 1 4889e5 REX.W movq rbp,rsp >> 0x2df967b05184 4 56 push rsi >> 0x2df967b05185 5 57 push rdi >> 0x2df967b05186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] >> 0x2df967b0518d d 0f866900 jna 0x2df967b051fc <+0x7c> >> 0x2df967b0519313 488b75f8 REX.W movq rsi,[rbp-0x8] >> 0x2df967b0519717 48b80300 REX.W movq rax,0x3 >> 0x2df967b051a121 488b5510 REX.W movq rdx,[rbp+0x10] >> 0x2df967b051a525 498b8dd005 REX.W movq rcx,[r13+0x5d0] >> 0x2df967b051ac2c 488bd9 REX.W movq rbx,rcx >> 0x2df967b051af2f e8ccd7f4ff call 0x2df967a52980 >> (LoadICTrampoline);; code: LOAD_IC >> 0x2df967b051b434 a801 test al,0x1 >> 0x2df967b051b636 0f855700 jnz 0x2df967b05213 <+0x93> >> 0x2df967b051bc3c 488bd8 REX.W movq rbx,rax >> 0x2df967b051bf3f 48c1eb20 REX.W shrq rbx, 32 >> 0x2df967b051c343 83ebff subl rbx,0xff >> 0x2df967b051c646 0f804c00 jo 0x2df967b05218 <+0x98> >> 0x2df967b051cc4c 48c1e320 REX.W shlq rbx, 32 >> 0x2df967b051d050 48bf0500 REX.W movq rdi,0x5 >>
Re: [v8-users] Re: Is "extend" keyword a performance antipattern???
So yes, the load/store for `this.done++` can't be reduced to simple machine ops when `doIt` is megamorphic, as far as I can tell. (5+ receiver maps should make the load/stores megamorphic, or at least that's what https://github.com/v8/v8/blob/9a0d5d9700ee269cbe7abee6d62733163dadac14/src/ic/ic.h#L33-L35 seems to indicate). That doesn't necessarily mean LoadIC is going to be slow, I guess it depends on how often you the receiver map is missing from the cache. Slower than the inlined monomorphic in-object field load/store, but probably not "anti-pattern" slow. I could be wrong about this, but I think the CallIC for individual calls to `doIt` can be inlined with different type feedback information for the load/store, so if that shows up in hot code which is monomorphic or polymorphic, it might produce better code than your example there. That said, I'm half guessing about all of this, so I'll leave the rest of this thread for more knowledgeable people. On Sunday, September 24, 2017 at 12:07:58 PM UTC-4, Bogdan Orlov wrote: > > After removing all intermediate %OptimizeFunctionOnNextCall() and putting > at the end (after usage of all Animal subclasses for enough feedback) like > this > > class Animal { > constructor(){ > this.done = 0 > } > doIt(){ > this.done++ > } > } > class Cat extends Animal {} > class Dog extends Animal {} > class Dog1 extends Animal {} > class Dog2 extends Animal {} > class Dog3 extends Animal {} > > function test(){ > var cat = new Cat(); > cat.doIt(); > cat.doIt(); > cat.doIt(); > var dog = new Dog(); > dog.doIt(); > dog.doIt(); > dog.doIt(); > var dog1 = new Dog1(); > dog1.doIt(); > dog1.doIt(); > dog1.doIt(); > var dog2 = new Dog2(); > dog2.doIt(); > dog2.doIt(); > dog2.doIt(); > var dog3 = new Dog3(); > dog3.doIt(); > dog3.doIt(); > dog3.doIt(); > %OptimizeFunctionOnNextCall(dog3.doIt) > dog3.doIt() > } > test() > > I got this output: > > 0x2df967b05180 0 55 push rbp > 0x2df967b05181 1 4889e5 REX.W movq rbp,rsp > 0x2df967b05184 4 56 push rsi > 0x2df967b05185 5 57 push rdi > 0x2df967b05186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] > 0x2df967b0518d d 0f866900 jna 0x2df967b051fc <+0x7c> > 0x2df967b0519313 488b75f8 REX.W movq rsi,[rbp-0x8] > 0x2df967b0519717 48b80300 REX.W movq rax,0x3 > 0x2df967b051a121 488b5510 REX.W movq rdx,[rbp+0x10] > 0x2df967b051a525 498b8dd005 REX.W movq rcx,[r13+0x5d0] > 0x2df967b051ac2c 488bd9 REX.W movq rbx,rcx > 0x2df967b051af2f e8ccd7f4ff call 0x2df967a52980 > (LoadICTrampoline);; code: LOAD_IC > 0x2df967b051b434 a801 test al,0x1 > 0x2df967b051b636 0f855700 jnz 0x2df967b05213 <+0x93> > 0x2df967b051bc3c 488bd8 REX.W movq rbx,rax > 0x2df967b051bf3f 48c1eb20 REX.W shrq rbx, 32 > 0x2df967b051c343 83ebff subl rbx,0xff > 0x2df967b051c646 0f804c00 jo 0x2df967b05218 <+0x98> > 0x2df967b051cc4c 48c1e320 REX.W shlq rbx, 32 > 0x2df967b051d050 48bf0500 REX.W movq rdi,0x5 > 0x2df967b051da5a 488b5510 REX.W movq rdx,[rbp+0x10] > 0x2df967b051de5e 498b8dd005 REX.W movq rcx,[r13+0x5d0] > 0x2df967b051e565 488bc3 REX.W movq rax,rbx > 0x2df967b051e868 488b75f8 REX.W movq rsi,[rbp-0x8] > 0x2df967b051ec6c e8cf1ff5ff call 0x2df967a571c0 > (StoreICStrictTrampoline);; code: STORE_IC > 0x2df967b051f171 498b45a0 REX.W movq rax,[r13-0x60] > 0x2df967b051f575 488be5 REX.W movq rsp,rbp > 0x2df967b051f878 5d pop rbp > 0x2df967b051f979 c20800 ret 0x8 > 0x2df967b051fc7c 48bbe07c89000100 REX.W movq rbx,0x100897ce0 > 0x2df967b0520686 33c0 xorl rax,rax > 0x2df967b0520888 488b75f8 REX.W movq rsi,[rbp-0x8] > 0x2df967b0520c8c e88ff4e7ff call 0x2df9679846a0 ;; code: > STUB, CEntryStub, minor: 8 > 0x2df967b0521191 eb80 jmp 0x2df967b05193 <+0x13> > 0x2df967b0521393 e8fcedcfff call 0x2df967804014 ;; debug: > deopt position, script offset '130' > ;; debug: > deopt position, inlining id '-1' > ;; debug: > deopt reason 'not a Smi' > ;; debug: > deopt index 2 > ;; > deoptimization bailout 2 > 0x2df967b0521898 e801eecfff call 0x2df96780401e ;; debug: > deopt position, script offset '130' > ;; debug: > deopt position, inlining id '-1' > ;; debug: > deopt reason 'overflow' >
Re: [v8-users] Re: Is "extend" keyword a performance antipattern???
After removing all intermediate %OptimizeFunctionOnNextCall() and putting at the end (after usage of all Animal subclasses for enough feedback) like this class Animal { constructor(){ this.done = 0 } doIt(){ this.done++ } } class Cat extends Animal {} class Dog extends Animal {} class Dog1 extends Animal {} class Dog2 extends Animal {} class Dog3 extends Animal {} function test(){ var cat = new Cat(); cat.doIt(); cat.doIt(); cat.doIt(); var dog = new Dog(); dog.doIt(); dog.doIt(); dog.doIt(); var dog1 = new Dog1(); dog1.doIt(); dog1.doIt(); dog1.doIt(); var dog2 = new Dog2(); dog2.doIt(); dog2.doIt(); dog2.doIt(); var dog3 = new Dog3(); dog3.doIt(); dog3.doIt(); dog3.doIt(); %OptimizeFunctionOnNextCall(dog3.doIt) dog3.doIt() } test() I got this output: 0x2df967b05180 0 55 push rbp 0x2df967b05181 1 4889e5 REX.W movq rbp,rsp 0x2df967b05184 4 56 push rsi 0x2df967b05185 5 57 push rdi 0x2df967b05186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] 0x2df967b0518d d 0f866900 jna 0x2df967b051fc <+0x7c> 0x2df967b0519313 488b75f8 REX.W movq rsi,[rbp-0x8] 0x2df967b0519717 48b80300 REX.W movq rax,0x3 0x2df967b051a121 488b5510 REX.W movq rdx,[rbp+0x10] 0x2df967b051a525 498b8dd005 REX.W movq rcx,[r13+0x5d0] 0x2df967b051ac2c 488bd9 REX.W movq rbx,rcx 0x2df967b051af2f e8ccd7f4ff call 0x2df967a52980 (LoadICTrampoline);; code: LOAD_IC 0x2df967b051b434 a801 test al,0x1 0x2df967b051b636 0f855700 jnz 0x2df967b05213 <+0x93> 0x2df967b051bc3c 488bd8 REX.W movq rbx,rax 0x2df967b051bf3f 48c1eb20 REX.W shrq rbx, 32 0x2df967b051c343 83ebff subl rbx,0xff 0x2df967b051c646 0f804c00 jo 0x2df967b05218 <+0x98> 0x2df967b051cc4c 48c1e320 REX.W shlq rbx, 32 0x2df967b051d050 48bf0500 REX.W movq rdi,0x5 0x2df967b051da5a 488b5510 REX.W movq rdx,[rbp+0x10] 0x2df967b051de5e 498b8dd005 REX.W movq rcx,[r13+0x5d0] 0x2df967b051e565 488bc3 REX.W movq rax,rbx 0x2df967b051e868 488b75f8 REX.W movq rsi,[rbp-0x8] 0x2df967b051ec6c e8cf1ff5ff call 0x2df967a571c0 (StoreICStrictTrampoline);; code: STORE_IC 0x2df967b051f171 498b45a0 REX.W movq rax,[r13-0x60] 0x2df967b051f575 488be5 REX.W movq rsp,rbp 0x2df967b051f878 5d pop rbp 0x2df967b051f979 c20800 ret 0x8 0x2df967b051fc7c 48bbe07c89000100 REX.W movq rbx,0x100897ce0 0x2df967b0520686 33c0 xorl rax,rax 0x2df967b0520888 488b75f8 REX.W movq rsi,[rbp-0x8] 0x2df967b0520c8c e88ff4e7ff call 0x2df9679846a0 ;; code: STUB, CEntryStub, minor: 8 0x2df967b0521191 eb80 jmp 0x2df967b05193 <+0x13> 0x2df967b0521393 e8fcedcfff call 0x2df967804014 ;; debug: deopt position, script offset '130' ;; debug: deopt position, inlining id '-1' ;; debug: deopt reason 'not a Smi' ;; debug: deopt index 2 ;; deoptimization bailout 2 0x2df967b0521898 e801eecfff call 0x2df96780401e ;; debug: deopt position, script offset '130' ;; debug: deopt position, inlining id '-1' ;; debug: deopt reason 'overflow' ;; debug: deopt index 3 ;; deoptimization bailout 3 0x2df967b0521d9d 90 nop 0x2df967b0521e9e 90 nop 0x2df967b0521f9f 90 nop 0x2df967b05220a0 90 nop 0x2df967b05221a1 90 nop 0x2df967b05222a2 90 nop 0x2df967b05223a3 90 nop 0x2df967b05224a4 90 nop 0x2df967b05225a5 90 nop 0x2df967b05226a6 90 nop 0x2df967b05227a7 90 nop 0x2df967b05228a8 90 nop 0x2df967b05229a9 90 nop 0x2df967b0522aaa 6690 nop So the result is the same (but without intermediate deopts) - v8 compiles 'this.done++' to slow polymorphic runtime calls LoadICTrampoline and StoreICStrictTrampoline. And if this is not a bug and v8 just works this way, we have slow polymorphic accessing to 'this' object in all base class methods in commonly used inheritance and polymorphism pattern and thus 'extends' keyword come out as performance antipattern On Sunday, September 24, 2017 at 4:31:44 PM UTC+3, Caitlin Potter wrote: > > I think
Re: [v8-users] Re: Is "extend" keyword a performance antipattern???
I think %OptimizeFunctionOnNextCall() is particularly bad for benchmarks, because you train the function to think the method load is monomorphic. With more type feedback, this should do a bit better, I think we can inline a finite amount of polymorphic loads. At least, I _think_ so. > On Sep 24, 2017, at 3:12 AM, B. Orlovwrote: > > Ok, there is more details. This is simplified example without loops > > class Animal { > constructor(){ > this.done = 0 > } > doIt(){ > this.done++ > } > } > > class Cat extends Animal {} > > class Dog extends Animal {} > class Dog1 extends Animal {} > class Dog2 extends Animal {} > class Dog3 extends Animal {} > > class AnimalCat { > constructor(){ > this.done = 0 > } > doIt(){ > this.done++ > } > } > > function test(){ > var cat = new Cat(); > cat.doIt(); > cat.doIt(); //warm > %OptimizeFunctionOnNextCall(cat.doIt) > cat.doIt(); //fast REX.W asm instruction for this.done read and write > > var dog = new Dog(); > dog.doIt(); //deopt - wrong map > %OptimizeFunctionOnNextCall(dog.doIt) > dog.doIt(); //the same but now added check for new hidden map - Dog > > var dog1 = new Dog1(); > dog1.doIt(); //deopt - wrong map > %OptimizeFunctionOnNextCall(dog1.doIt) > dog1.doIt() //the same but now added one more check for new hidden map - > Dog1 > > var dog2 = new Dog2(); > dog2.doIt(); //deopt - wrong map > %OptimizeFunctionOnNextCall(dog2.doIt) > dog2.doIt() //the same but now added one more check for new hidden map - > Dog2 > > var dog3 = new Dog3(); > dog3.doIt(); //deopt - wrong map > %OptimizeFunctionOnNextCall(dog3.doIt) > dog3.doIt() //v8 gives up and compiles this.done to slow calls c++ runtime > functions - LoadICTrampoline and StoreICStrictTrampoline > } > > test() > > Running with latest node with arguments "node --trace-deopt --print-opt-code > --allow-natives-syntax test.js" there will be output of asm instructions for > each optimization of doIt() method. After first optimizations (with only Cat > class invoking) I got > > 0x3e9869385180 0 55 push rbp > 0x3e9869385181 1 4889e5 REX.W movq rbp,rsp > 0x3e9869385184 4 56 push rsi > 0x3e9869385185 5 57 push rdi > 0x3e9869385186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] > 0x3e986938518d d 0f863f00 jna 0x3e98693851d2 <+0x52> > 0x3e986938519313 488b4510 REX.W movq rax,[rbp+0x10] > 0x3e986938519717 a801 test al,0x1 > 0x3e986938519919 0f844a00 jz 0x3e98693851e9 <+0x69> > 0x3e986938519f1f 48bb9112642f9b32 REX.W movq rbx,0x329b2f641291 > ;; object: 0x329b2f641291
[v8-users] Re: Is "extend" keyword a performance antipattern???
Ok, there is more details. This is simplified example without loops class Animal { constructor(){ this.done = 0 } doIt(){ this.done++ } } class Cat extends Animal {} class Dog extends Animal {} class Dog1 extends Animal {} class Dog2 extends Animal {} class Dog3 extends Animal {} class AnimalCat { constructor(){ this.done = 0 } doIt(){ this.done++ } } function test(){ var cat = new Cat(); cat.doIt(); cat.doIt(); //warm %OptimizeFunctionOnNextCall(cat.doIt) cat.doIt(); //fast REX.W asm instruction for this.done read and write var dog = new Dog(); dog.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog.doIt) dog.doIt(); //the same but now added check for new hidden map - Dog var dog1 = new Dog1(); dog1.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog1.doIt) dog1.doIt() //the same but now added one more check for new hidden map - Dog1 var dog2 = new Dog2(); dog2.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog2.doIt) dog2.doIt() //the same but now added one more check for new hidden map - Dog2 var dog3 = new Dog3(); dog3.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog3.doIt) dog3.doIt() //v8 gives up and compiles this.done to slow calls c++ runtime functions - LoadICTrampoline and StoreICStrictTrampoline } test() Running with latest node with arguments "node --trace-deopt --print-opt-code --allow-natives-syntax test.js" there will be output of asm instructions for each optimization of doIt() method. After first optimizations (with only Cat class invoking) I got 0x3e9869385180 0 55 push rbp 0x3e9869385181 1 4889e5 REX.W movq rbp,rsp 0x3e9869385184 4 56 push rsi 0x3e9869385185 5 57 push rdi 0x3e9869385186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] 0x3e986938518d d 0f863f00 jna 0x3e98693851d2 <+0x52> 0x3e986938519313 488b4510 REX.W movq rax,[rbp+0x10] 0x3e986938519717 a801 test al,0x1 0x3e986938519919 0f844a00 jz 0x3e98693851e9 <+0x69> 0x3e986938519f1f 48bb9112642f9b32 REX.W movq rbx,0x329b2f641291 ;; object: 0x329b2f6412910x3e98693851a929 483958ff REX.W cmpq [rax-0x1],rbx 0x3e98693851ad2d 0f853b00 jnz 0x3e98693851ee <+0x6e> 0x3e98693851b333 8b581b movl rbx,[rax+0x1b] 0x3e98693851b636 83ebff subl rbx,0xff 0x3e98693851b939 0f803400 jo 0x3e98693851f3 <+0x73> 0x3e98693851bf3f 48c1e320 REX.W shlq rbx, 32 0x3e98693851c343 48895817 REX.W movq [rax+0x17],rbx 0x3e98693851c747 498b45a0 REX.W movq rax,[r13-0x60] 0x3e98693851cb4b 488be5 REX.W movq rsp,rbp 0x3e98693851ce4e 5d pop rbp 0x3e98693851cf4f c20800 ret 0x8 than after few deoptimizations for Dog2 class V8 compiles doIt() method to this 0x3e9869385540 0 55 push rbp 0x3e9869385541 1 4889e5 REX.W movq rbp,rsp 0x3e9869385544 4 56 push rsi 0x3e9869385545 5 57 push rdi 0x3e9869385546 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] 0x3e986938554d d 0f867b00 jna 0x3e98693855ce <+0x8e> 0x3e986938555313 488b4510 REX.W movq rax,[rbp+0x10] 0x3e986938555717 a801 test al,0x1 0x3e986938555919 0f848900 jz 0x3e98693855e8 <+0xa8> 0x3e986938555f1f 488b58ff REX.W movq rbx,[rax-0x1] 0x3e986938556323 48ba9112642f9b32 REX.W movq rdx,0x329b2f641291 ;; object: 0x329b2f641291 0x3e986938556d2d 483bd3 REX.W cmpq rdx,rbx 0x3e986938557030 0f843900 jz 0x3e98693855af <+0x6f> 0x3e986938557636 48ba4914642f9b32 REX.W movq rdx,0x329b2f641449 ;; object: 0x329b2f641449 0x3e986938558040 483bd3 REX.W cmpq rdx,rbx 0x3e986938558343 0f842600 jz 0x3e98693855af <+0x6f> 0x3e986938558949 48ba5115642f9b32 REX.W movq rdx,0x329b2f641551 ;; object: 0x329b2f641551 0x3e986938559353 483bd3 REX.W cmpq rdx,rbx 0x3e986938559656 0f841300 jz 0x3e98693855af <+0x6f> 0x3e986938559c5c 48ba5916642f9b32 REX.W movq rdx,0x329b2f641659 ;; object: 0x329b2f641659 0x3e98693855a666 483bd3 REX.W cmpq rdx,rbx 0x3e98693855a969 0f853e00 jnz 0x3e98693855ed <+0xad> 0x3e98693855af6f 8b581b movl rbx,[rax+0x1b] 0x3e98693855b272 83ebff subl rbx,0xff 0x3e98693855b575 0f803700 jo 0x3e98693855f2 <+0xb2> 0x3e98693855bb7b 48c1e320 REX.W shlq rbx, 32 0x3e98693855bf7f 48895817 REX.W movq [rax+0x17],rbx 0x3e98693855c383 498b45a0 REX.W movq rax,[r13-0x60] 0x3e98693855c787 488be5 REX.W movq rsp,rbp 0x3e98693855ca8a 5d pop rbp 0x3e98693855cb8b
[v8-users] Re: Is "extend" keyword a performance antipattern???
Ok, I there is more details. This is simplified example without loops class Animal { constructor(){ this.done = 0 } doIt(){ this.done++ } } class Cat extends Animal {} class Dog extends Animal {} class Dog1 extends Animal {} class Dog2 extends Animal {} class Dog3 extends Animal {} class AnimalCat { constructor(){ this.done = 0 } doIt(){ this.done++ } } function test(){ var cat = new Cat(); cat.doIt(); cat.doIt(); //warm %OptimizeFunctionOnNextCall(cat.doIt) cat.doIt(); //fast REX.W asm instruction for this.done read and write var dog = new Dog(); dog.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog.doIt) dog.doIt(); //the same but now added check for new hidden map - Dog var dog1 = new Dog1(); dog1.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog1.doIt) dog1.doIt() //the same but now added one more check for new hidden map - Dog1 var dog2 = new Dog2(); dog2.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog2.doIt) dog2.doIt() //the same but now added one more check for new hidden map - Dog2 var dog3 = new Dog3(); dog3.doIt(); //deopt - wrong map %OptimizeFunctionOnNextCall(dog3.doIt) dog3.doIt() //v8 gives up and compiles this.done to slow calls c++ runtime functions - LoadICTrampoline and StoreICStrictTrampoline } test() Running with latest node with arguments "node --trace-deopt --print-opt-code --allow-natives-syntax test.js" there will be output of asm instructions for each optimization of doIt() method. After first optimizations (with only Cat class invoking) I got 0x3e9869385180 0 55 push rbp 0x3e9869385181 1 4889e5 REX.W movq rbp,rsp 0x3e9869385184 4 56 push rsi 0x3e9869385185 5 57 push rdi 0x3e9869385186 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] 0x3e986938518d d 0f863f00 jna 0x3e98693851d2 <+0x52> 0x3e986938519313 488b4510 REX.W movq rax,[rbp+0x10] 0x3e986938519717 a801 test al,0x1 0x3e986938519919 0f844a00 jz 0x3e98693851e9 <+0x69> 0x3e986938519f1f 48bb9112642f9b32 REX.W movq rbx,0x329b2f641291 ;; object: 0x329b2f6412910x3e98693851a929 483958ff REX.W cmpq [rax-0x1],rbx 0x3e98693851ad2d 0f853b00 jnz 0x3e98693851ee <+0x6e> 0x3e98693851b333 8b581b movl rbx,[rax+0x1b] 0x3e98693851b636 83ebff subl rbx,0xff 0x3e98693851b939 0f803400 jo 0x3e98693851f3 <+0x73> 0x3e98693851bf3f 48c1e320 REX.W shlq rbx, 32 0x3e98693851c343 48895817 REX.W movq [rax+0x17],rbx 0x3e98693851c747 498b45a0 REX.W movq rax,[r13-0x60] 0x3e98693851cb4b 488be5 REX.W movq rsp,rbp 0x3e98693851ce4e 5d pop rbp 0x3e98693851cf4f c20800 ret 0x8 than after few deoptimizations for Dog2 class V8 compiles doIt() method to this 0x3e9869385540 0 55 push rbp 0x3e9869385541 1 4889e5 REX.W movq rbp,rsp 0x3e9869385544 4 56 push rsi 0x3e9869385545 5 57 push rdi 0x3e9869385546 6 493ba5480c REX.W cmpq rsp,[r13+0xc48] 0x3e986938554d d 0f867b00 jna 0x3e98693855ce <+0x8e> 0x3e986938555313 488b4510 REX.W movq rax,[rbp+0x10] 0x3e986938555717 a801 test al,0x1 0x3e986938555919 0f848900 jz 0x3e98693855e8 <+0xa8> 0x3e986938555f1f 488b58ff REX.W movq rbx,[rax-0x1] 0x3e986938556323 48ba9112642f9b32 REX.W movq rdx,0x329b2f641291 ;; object: 0x329b2f641291 0x3e986938556d2d 483bd3 REX.W cmpq rdx,rbx 0x3e986938557030 0f843900 jz 0x3e98693855af <+0x6f> 0x3e986938557636 48ba4914642f9b32 REX.W movq rdx,0x329b2f641449 ;; object: 0x329b2f641449 0x3e986938558040 483bd3 REX.W cmpq rdx,rbx 0x3e986938558343 0f842600 jz 0x3e98693855af <+0x6f> 0x3e986938558949 48ba5115642f9b32 REX.W movq rdx,0x329b2f641551 ;; object: 0x329b2f641551 0x3e986938559353 483bd3 REX.W cmpq rdx,rbx 0x3e986938559656 0f841300 jz 0x3e98693855af <+0x6f> 0x3e986938559c5c 48ba5916642f9b32 REX.W movq rdx,0x329b2f641659 ;; object: 0x329b2f641659 0x3e98693855a666 483bd3 REX.W cmpq rdx,rbx 0x3e98693855a969 0f853e00 jnz 0x3e98693855ed <+0xad> 0x3e98693855af6f 8b581b movl rbx,[rax+0x1b] 0x3e98693855b272 83ebff subl rbx,0xff 0x3e98693855b575 0f803700 jo 0x3e98693855f2 <+0xb2> 0x3e98693855bb7b 48c1e320 REX.W shlq rbx, 32 0x3e98693855bf7f 48895817 REX.W movq [rax+0x17],rbx 0x3e98693855c383 498b45a0 REX.W movq rax,[r13-0x60] 0x3e98693855c787 488be5 REX.W movq rsp,rbp 0x3e98693855ca8a 5d pop rbp 0x3e98693855cb8b
[v8-users] Re: Is "extend" keyword a performance antipattern???
Microbenchmarks are infamously difficult to get right as often you're not testing what you think you're testing. Are you sure the optimizer isn't just throwing away code in some cases, since you're not actually doing any work with the `done` property? There's no reason that your code even has to run unless I'm reading it wrong. And it's not like C++ where you can look at the generated instructions to see what the optimizer is doing.. On Friday, September 22, 2017 at 9:24:00 PM UTC-7, B. Orlov wrote: > > Take a look at commonly used in oop inheritance pattern for extending base > class: > > class Animal { > constructor(){ > this.done = 0 > } > doIt(){ > this.done++ > } > } > > class Cat extends Animal {} > class Dog extends Animal {} > class Dog1 extends Animal {} > class Dog2 extends Animal {} > class Dog3 extends Animal {} > > function testAnimal(animal){ > for(var i = 0; i < 10; i++){ > animal.doIt(); > } > } > > > function test(){ > var cat = new Cat(); > testAnimal(cat) > var dog = new Dog(); > testAnimal(dog) > var dog1 = new Dog1(); > testAnimal(dog1) > var dog2 = new Dog2(); > testAnimal(dog2) > var dog3 = new Dog3(); > testAnimal(dog3) > } > > test() > > Running in latest node (6.0.287.53 v8 version) I get the following results: > Invoking testAnimal function with first descendant class Cat, V8 does a > great job by compiling to doIt() method and "this.done++" incrementation to > REX.W asm instruction without any calls to slow-runtime c++ function for > generic field access.Than, by invoking doIt() on second descendant class > Dog, V8 fall down to doIt() method deoptimization (and all outer functions > which also were inlined) and add few ams rew.x and one jz instruction to > check hidden map for second Dog class. Than on invoking doIt() on each new > subclass v8 again fall down to deoptimization and add new checks for new > hidden map and finally on fourth descendant class Dog3 v8 give up and for > "this.done++" goes to call LoadICTrampoline StoreICStrictTrampoline c++ > runtime function for generic access. I hope its only bug and v8 can > efficient deal with inheritance and accessing field without slow runtime > generic filed access otherwise the big question came in - is "extend" > keyword a performance antipattern and why v8 can't implement something like > c++ v-table mechanism ? > -- -- v8-users mailing list v8-users@googlegroups.com http://groups.google.com/group/v8-users --- You received this message because you are subscribed to the Google Groups "v8-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.