Bug 240290

Summary: [JSC] Add NoIndexing miss optimization
Product: WebKit Reporter: Jarred Sumner <jarred>
Component: JavaScriptCoreAssignee: Yusuke Suzuki <ysuzuki>
Status: RESOLVED FIXED    
Severity: Normal CC: saam, webkit-bug-importer, ysuzuki
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=239936
Attachments:
Description Flags
microbenchmark.js none

Description Jarred Sumner 2022-05-10 15:14:30 PDT
Created attachment 459136 [details]
microbenchmark.js

Two fast paths for Array.from:

1. Similar to https://bugs.webkit.org/show_bug.cgi?id=239936, a fast path for Array.from (ArrayConstructor) given an array of Int32 or double that uses something like:
- @appendMemcpy
- Array.prototype.slice

If the array iterator protocol is unmodified. 

2. Given input like this: Array.from({length: number}), it could skip the loop that sets undefined on each element

A microbenchmark shows that for new Array(length) is about 32x faster than Array.from({length})


❯ jsc /tmp/a.js
new Array(2097152): 79.586ms
Array.from({length: 2097152}): 2,595.839ms

You can see this pattern is fairly common on GitHub: https://sourcegraph.com/search?q=context:global+Array.from%28%7Blength&patternType=literal


Also:

When there is `items.@@iterator`, is it intentional that the array isn't initialized with a length? Could it read the length if defined? 
> var result = this !== @Array && @isConstructor(this) ? new this() : [];


https://github.com/WebKit/WebKit/blob/main/Source/JavaScriptCore/builtins/ArrayConstructor.js#L60
Comment 1 Radar WebKit Bug Importer 2022-05-17 15:15:21 PDT
<rdar://problem/93456330>
Comment 2 Yusuke Suzuki 2022-07-28 17:10:30 PDT
Right. Probably this is not so hard.
Comment 3 Yusuke Suzuki 2022-07-30 01:23:02 PDT
Ah, probably the current implementation is enough optimized for the attached benchmark's pattern.
Two code is actually doing a bit different thing.
The first one is holes. On the other hand, the second one is filled with undefined.
So,

>>> 1 in new Array(30)
false
>>> 1 in Array.from({length:30})
true

I wonder if we should optimize `({})[0]` case more. Currently, I think we are invoking  operationGetByVal.

Hottest bytecodes as <numSamples   'functionName#hash:JITType:bytecodeIndex'>
   900    'operationGetByVal#<nil>:None:<nil>'
   413    'JSC::JSObject::getOwnPropertySlotByIndex(JSC::JSObject*, JSC::JSGlobalObject*, unsigned int, JSC::PropertySlot&)#<nil>:None:<nil>'
    61    'from#<nil>:FTL:bc#368'
    59    'from#<nil>:FTL:bc#426'
    44    'JSC::JSArray::tryCreate(JSC::VM&, JSC::Structure*, unsigned int, unsigned int)#<nil>:None:<nil>'
     6    'syscall_thread_switch#<nil>:None:<nil>'
Comment 4 Yusuke Suzuki 2022-07-30 03:16:35 PDT
Pull request: https://github.com/WebKit/WebKit/pull/2876
Comment 5 EWS 2022-08-04 10:46:14 PDT
Committed 253120@main (801dcc1c9e7a): <https://commits.webkit.org/253120@main>

Reviewed commits have been landed. Closing PR #2876 and removing active labels.