Bug 232479

Summary: [JSC] Public Class Field initialization is slow
Product: WebKit Reporter: Rob Palmer <rob.palmer2>
Component: JavaScriptCoreAssignee: Yusuke Suzuki <ysuzuki>
Status: RESOLVED FIXED    
Severity: Normal CC: ashvayka, benjamin, caitp, cdumez, cmarcelo, ews-watchlist, fpizlo, keith_miller, mark.lam, msaboff, saam, ticaiolima, tzagallo, webkit-bug-importer, xan.lopez, ysuzuki
Priority: P2 Keywords: InRadar
Version: Safari Technology Preview   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=198330
Attachments:
Description Flags
Field Initialization Benchmark
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch ashvayka: review+

Description Rob Palmer 2021-10-29 00:53:29 PDT
Created attachment 442791 [details]
Field Initialization Benchmark

I benchmarked ES2022 class field initialization time.

  https://gist.github.com/robpalme/a9ff3e4764b764d7dc24743816d8a624#gistcomment-3944128

In Chrome and Firefox, public field initialization has been optimized to have similar performance to private fields.  Performance is also similar to assigning to `this` inside the constructor.

In latest Safari Tech Preview 134, performance of public field initialization is 12x worse than both private fields and constructor assignment.

- Classic Constructor Assignments:  38ms
- Public Fields:  462ms
- Private Fields:  33ms
Comment 1 Caitlin Potter (:caitp) 2021-11-02 07:58:24 PDT
Currently, public fields are implemented in LLint as op_define_data_property and op_define_accessor_property, both of which seem to be implemented only as slow paths with no caching, and as C++ calls with no caching in each JIT.

It may be worth adding some caching and inlining for these operations.
Comment 2 Radar WebKit Bug Importer 2021-11-05 00:54:16 PDT
<rdar://problem/85050031>
Comment 3 Yusuke Suzuki 2021-11-23 23:01:52 PST
*** Bug 198330 has been marked as a duplicate of this bug. ***
Comment 4 Yusuke Suzuki 2021-11-23 23:35:07 PST
Yeah, looks like public-class-field implementation lacks this optimization.
Comment 5 Yusuke Suzuki 2021-11-23 23:39:26 PST
Created attachment 445071 [details]
Patch
Comment 6 Caitlin Potter (:caitp) 2021-11-24 06:50:32 PST
Comment on attachment 445071 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=445071&action=review

> Source/JavaScriptCore/runtime/JSObject.h:1590
> +    return putDirectInternal<PutModeDefineOwnPropertyIgnoringExtensibility>(vm, propertyName, value, attributes, slot);

Under which conditions do we end up with the "IgnoringExtensibility" flag? Is this a type speculation to avoid unnecessary checks in DFG?

Under normal circumstances, defining public class fields should be concerned with extensibility, particularly for derived classes
Comment 7 Yusuke Suzuki 2021-11-24 22:14:21 PST
Comment on attachment 445071 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=445071&action=review

>> Source/JavaScriptCore/runtime/JSObject.h:1590
>> +    return putDirectInternal<PutModeDefineOwnPropertyIgnoringExtensibility>(vm, propertyName, value, attributes, slot);
> 
> Under which conditions do we end up with the "IgnoringExtensibility" flag? Is this a type speculation to avoid unnecessary checks in DFG?
> 
> Under normal circumstances, defining public class fields should be concerned with extensibility, particularly for derived classes

For now, I would like to keep the current putDirect semantics since this patch's intention is not changing that.
Probably, I'll look into it to remove that hack for putDirect. But for now, I keep that semantics and adding the new cleaner one.
Comment 8 Yusuke Suzuki 2021-11-24 22:15:20 PST
Created attachment 445119 [details]
Patch
Comment 9 Yusuke Suzuki 2021-11-24 22:18:12 PST
Created attachment 445120 [details]
Patch
Comment 10 Yusuke Suzuki 2021-11-24 22:27:22 PST
Created attachment 445121 [details]
Patch
Comment 11 Yusuke Suzuki 2021-11-25 17:37:26 PST
Created attachment 445163 [details]
Patch
Comment 12 Alexey Shvayka 2021-11-29 09:23:38 PST
Comment on attachment 445163 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=445163&action=review

r=me with a tiny nit on FIXME.

So nice to see generator.emitCallDefineProperty() gone! I've evaluated pretty much the same approach to replace them in ClassExprNode::emitBytecode() but that didn't result in microbenchmark progression.

Also, really cool to have JSObject::putDirectInternal() return an error message, improving developer experience with JSC.

> Source/JavaScriptCore/runtime/CommonSlowPaths.h:202
> +    if (LIKELY(propertyName != vm.propertyNames->underscoreProto && !structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && (isJSFunction || structure->classInfo()->methodTable.defineOwnProperty == &JSObject::defineOwnProperty))) {

This handle non-reified accessors, great!

> Source/JavaScriptCore/runtime/JSObjectInlines.h:285
>      // FIXME: For a failure due to non-extensible structure, the error message is misleading

We can remove this FIXME now.
Comment 13 Yusuke Suzuki 2021-11-29 12:02:51 PST
Comment on attachment 445163 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=445163&action=review

Thank you!

>> Source/JavaScriptCore/runtime/CommonSlowPaths.h:202
>> +    if (LIKELY(propertyName != vm.propertyNames->underscoreProto && !structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && (isJSFunction || structure->classInfo()->methodTable.defineOwnProperty == &JSObject::defineOwnProperty))) {
> 
> This handle non-reified accessors, great!

:D

>> Source/JavaScriptCore/runtime/JSObjectInlines.h:285
>>      // FIXME: For a failure due to non-extensible structure, the error message is misleading
> 
> We can remove this FIXME now.

NICE.
Comment 14 Yusuke Suzuki 2021-11-29 12:04:21 PST
Committed r286251 (244614@main): <https://commits.webkit.org/244614@main>