facebook / hermes

A JavaScript engine optimized for running React Native.
https://hermesengine.dev/
MIT License
9.51k stars 604 forks source link

Array.protoype.indexOf() Performance Regression #1270

Open tctacm opened 5 months ago

tctacm commented 5 months ago

Bug Description

The Array.prototype.indexOf() functions seems to be significantly slower on Hermes when compared to JSC. I first noticed the issue in React Native 0.64 inside the JSTimers file: https://github.com/facebook/react-native/blob/0645c38014e8310d8e387dabc860c8c5878beb6e/Libraries/Core/Timers/JSTimers.js#L62 , however, I have reproduced a case using the Hermes CLI to verify my findings.

Hermes git revision (if applicable): release-v0.12? I'm using Hermes CLI with version 0.12.0 React Native version: N/A OS: macOS Sonoma 14.2.1 Platform (most likely one of arm64-v8a, armeabi-v7a, x86, x86_64): x86_64

Steps To Reproduce

The following code running on Hermes will take significantly longer:

index_of_test.js:

const ARR_SIZE = 10000;

// Make an array of ARR_SIZE increasing non-negative integers
// [0, 1, 2, 3, 4, 5, 6, 7, 8, ...]
var test_arr = [];
for (var i = 0; i < ARR_SIZE; i++) {
  test_arr.push(i);
}

// Call indexOf 1000 times with the last element in the array
var start = Date.now();
for (var i = 0; i < 1000; i++) {
  test_arr.indexOf(ARR_SIZE - 1);
}
var end = Date.now();
print('Array.indexOf last element time: ' + (end - start));

// Call indexOf 1000 times with the first element in the array
var start = Date.now();
for (var i = 0; i < 1000; i++) {
  test_arr.indexOf(0);
}
var end = Date.now();
print('Array.indexOf first element time: ' + (end - start));
  1. Run with hermes cli: hermes ./index_of_test.js
    Array.indexOf last element time: 253
    Array.indexOf first element time: 0
  2. Run with JSC cli: jsc --useJIT=false ./index_of_test.js
    Array.indexOf last element time: 3
    Array.indexOf first element time: 0

    My example seems to indicate that Hermes is much slower at iterating through and comparing Array values when compared to JSC.

Funnily enough, the same test using Array.prototype.includes() has similar performance between Hermes and JSC. I would assume (probably incorrectly) that the two functions use a similar algorithm. Although in this case, JSC is also much slower compared to its indexOf performance.

  1. Run with hermes ~/includes_test.js
Array.includes last element time: 122
Array.includes first element time: 0
  1. Run with jsc --useJIT=false ~/includes_test.js
Array.includes last element time: 116
Array.includes first element time: 0

The Expected Behavior

Hermes performance on Array.protoype.indexOf() should be on-par with JSC performance.

tmikov commented 5 months ago

Thank you for reporting this! We can reproduce it and will address it. This is most likely because Array.protoype.indexOf() doesn't have a fast-path checking for an array.

gituser8796 commented 4 months ago

Hi @tmikov , is there a target release for this one?

tmikov commented 4 months ago

React Native releases are not synchronized with Hermes is is hard to predict where anything would land. In any case, this task is open, but we haven't started working on it yet.

DerGuteMoritz commented 1 month ago

This finding also confirms this if I am not mistaken? If so, #1024 could also get fixed as a consequence of this here issue getting fixed.

tmikov commented 1 month ago

@DerGuteMoritz good point! I hadn't actually realized that.