Bug 246009

Summary: Object.entries() is 1.5x slower in JSC compared to V8
Product: WebKit Reporter: Jarred Sumner <jarred>
Component: JavaScriptCoreAssignee: Yusuke Suzuki <ysuzuki>
Status: RESOLVED FIXED    
Severity: Normal CC: mark.lam, webkit-bug-importer, ysuzuki
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Launch_bun_2022-10-03_23.22.34_2576143F.trace.zip
none
rollup.browser.js none

Jarred Sumner
Reported 2022-10-04 00:28:20 PDT
Created attachment 462781 [details] Launch_bun_2022-10-03_23.22.34_2576143F.trace.zip I've attached an Instruments trace and sampling profiler output of using Rollup.js to bundle the "@babel/standalone" npm package. End-to-end, it runs about 1.6x slower in JSC (bun) compared to V8 (node). Nearly all the time is spent in JSC. I think it's _mostly_ because of Object.entries() (which is why the issue is titled about Object.entries()). Rollup seems to use Object.entries() in their code a _lot_. In @babel/standalone's case, Object.entries() is called 550,000 times. For larger objects, Object.entries() is around 1.5x slower than in V8 (microbenchmark). It looks like ObjectKeys exists in DFGSpeculateJIT64.cpp but ObjectEntries and ObjectValues do not. Maybe adding it as a JIT operation would help? I don't know if that's the whole story though. It feels like there's a side effects tracking thing that would enable skipping some of the PropertyTable::find lookups, maybe? ``` ❯ bun /Users/jarred/Code/bun/bench/snippets/object-entries.mjs cpu: Apple M1 Max runtime: bun 0.1.14 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------- ----------------------------- Object.entries(26 keys) 1.27 µs/iter (1.21 µs … 1.99 µs) 1.28 µs 1.99 µs 1.99 µs Object.keys(26 keys) 515.77 ns/iter (477.57 ns … 628.68 ns) 526.63 ns 617.05 ns 628.68 ns Object.entries(2 keys) 132.46 ns/iter (125.25 ns … 205.22 ns) 127.54 ns 194.48 ns 196.87 ns Object.keys(2 keys) 4.68 ns/iter (4.12 ns … 219.59 ns) 4.42 ns 7.48 ns 10.22 ns ``` ``` ❯ node /Users/jarred/Code/bun/bench/snippets/object-entries.mjs cpu: Apple M1 Max runtime: node v18.9.1 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------- ----------------------------- Object.entries(26 keys) 830.02 ns/iter (803.19 ns … 1.07 µs) 821.85 ns 1.07 µs 1.07 µs Object.keys(26 keys) 327.72 ns/iter (319.58 ns … 421.81 ns) 329.03 ns 400.85 ns 421.81 ns Object.entries(2 keys) 98.28 ns/iter (96.05 ns … 452.63 ns) 97.19 ns 105.5 ns 108.87 ns Object.keys(2 keys) 13.36 ns/iter (10.75 ns … 76.29 ns) 15.3 ns 17.92 ns 21.13 ns ``` - Benchmark: ``` import { bench, run } from "../../node_modules/mitata/src/cli.mjs"; const obj = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26, }; bench("Object.entries(26 keys)", () => { var k; for (let [key, value] of Object.entries(obj)) { value = key; } return k; }); bench("Object.keys(26 keys)", () => { var k; for (let [key, value] of Object.keys(obj)) { value = key; } return k; }); bench("Object.entries(2 keys)", () => { var k; for (let [key, value] of Object.entries({ a: 1, b: 2 })) { value = key; } return k; }); bench("Object.keys(2 keys)", () => { var k; for (let item of Object.keys({ a: 1, b: 2 })) { } return k; }); await run(); ``` Sampling profiler output: Sampling rate: 100.000000 microseconds. Total samples: 19077 Top functions as <numSamples 'functionName#hash:sourceID'> 4028 'include#<nil>:5' 1552 'parseNode#<nil>:5' 1299 '#<nil>:5' 1089 'entries#<nil>:4294967295' 979 'includeCallArguments#<nil>:5' 809 'create#<nil>:4294967295' 656 'hasEffects#<nil>:5' 606 'arrayIteratorNextHelper#<nil>:7' 568 'NodeBase#<nil>:5' 547 '#<nil>:4294967295' 506 'bind#<nil>:5' 495 'render#<nil>:5' Sampling rate: 100.000000 microseconds. Total samples: 19077 Tier breakdown: ----------------------------------- LLInt: 184 (0.964512%) Baseline: 935 (4.901190%) DFG: 3991 (20.920480%) FTL: 10469 (54.877601%) js builtin: 862 (4.518530%) Wasm: 0 (0.000000%) Host: 559 (2.930230%) RegExp: 115 (0.602820%) C/C++: 0 (0.000000%) Unknown Frame: 4 (0.020968%) Unknown Executable: 2820 (14.782198%) Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'> 1089 'entries#<nil>:None:<nil>' 1001 'include#<nil>:FTL:bc#33' 809 'create#<nil>:None:<nil>' 575 'parseNode#<nil>:FTL:bc#218' 547 '#<nil>:None:<nil>' 443 'parseNode#<nil>:FTL:bc#479' 411 'Set#<nil>:None:<nil>' 404 'include#<nil>:FTL:bc#107' 363 'include#<nil>:FTL:bc#296' 337 'include#<nil>:FTL:bc#93' 280 'arrayIteratorNextHelper#<nil>:FTL:bc#122' 272 'include#<nil>:FTL:bc#54' 251 'arrayIteratorNextHelper#<nil>:FTL:bc#58' 221 'includeCallArguments#<nil>:FTL:bc#59' 206 'includeProperties#<nil>:FTL:bc#59' 127 'Map#<nil>:None:<nil>' 117 'getEntities#<nil>:FTL:bc#180' 111 'include#<nil>:FTL:bc#0' 107 'parseModule#<nil>:None:<nil>' 106 'values#<nil>:None:<nil>' 106 'parseNode#<nil>:FTL:bc#760' 95 'includeCallArguments#<nil>:DFG:bc#201' 90 'include#<nil>:FTL:bc#138' 87 'getEntities#<nil>:FTL:bc#76' 84 'includeCallArguments#<nil>:FTL:bc#0' 81 'bind#<nil>:FTL:bc#262' 81 'delete#<nil>:None:<nil>' 81 'include#<nil>:FTL:bc#196' 77 '#<nil>:FTL:bc#0' 75 'createScope#<nil>:FTL:bc#11' 73 'include#<nil>:FTL:bc#27' 73 'include#<nil>:FTL:<nil>' 72 '/^(?:break|case|catch|continue|debugger|default|do|else|finally|for|function|if|return|switch|throw|try|var|while|with|null|true|false|instanceof|typeof|void|delete|new|in|this|const|class|extends|export|import|super)$/#<nil>:RegExp:<nil>' 72 'bind#<nil>:FTL:bc#65' 71 'includeCallArguments#<nil>:FTL:<nil>' 71 'includeProperties#<nil>:FTL:bc#19' 70 'include#<nil>:DFG:bc#54' 66 'includeProperties#<nil>:FTL:bc#70' 65 'includeUnknownTest#<nil>:DFG:bc#22' 65 'include#<nil>:FTL:bc#229'
Attachments
Launch_bun_2022-10-03_23.22.34_2576143F.trace.zip (895.90 KB, application/zip)
2022-10-04 00:28 PDT, Jarred Sumner
no flags
rollup.browser.js (387.09 KB, text/javascript)
2022-10-04 01:23 PDT, Jarred Sumner
no flags
Jarred Sumner
Comment 1 2022-10-04 01:23:40 PDT
Created attachment 462784 [details] rollup.browser.js Here is a build of Rollup.js that will run in JSC If you run `jsc -m rollup.browser.js` with a file named "file.js" in the same cwd it should build file.js If you uncomment the last line it will print it to the terminal, but since we are not benchmarking printing to stdout from this function it is commented out
Yusuke Suzuki
Comment 2 2022-10-04 11:04:24 PDT
Note 1. We should apply Object.assign’s optimization 2. We should wire Object.entries with own-keys cache to reuse string cells.
Radar WebKit Bug Importer
Comment 3 2022-10-04 15:10:22 PDT
Yusuke Suzuki
Comment 4 2022-10-11 23:58:28 PDT
EWS
Comment 5 2022-10-12 21:52:07 PDT
Committed 255470@main (2b999503e689): <https://commits.webkit.org/255470@main> Reviewed commits have been landed. Closing PR #5281 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.