perf: fast _getFreeIndex (#48925)

Summary:
The performance of `_getFreeIndex` is quite terrible since the `timersID` array can get quite large when you spawn a lot of promises or timers. We profiled our application for 28 seconds on RN 0.71.11 and noticed that the `indexOf` into this array was consuming almost a second.

The hermes version that we are using has a pretty slow  `indexOf` compared to other engines, and the static hermes will improve it by 12x but for the time being, this is a perf issue. https://github.com/facebook/hermes/pull/1447

We avoid having to use `indexOf` by maintaining a list of the free ids.

**Before - Samsung Galaxy A52 for 28 seconds of profiling**
![image](https://github.com/user-attachments/assets/e669ed62-58d8-4d11-8a85-a82816b3792d)

**After - Samsung Galaxy A52 for 28 seconds of profiling**
![image](https://github.com/user-attachments/assets/48cd369f-0887-4683-920c-43068d0228b0)

## Changelog:

[INTERNAL] [FIXED] - Improve performance of _getFreeIndex

Pull Request resolved: https://github.com/facebook/react-native/pull/48925

Test Plan: -  Tests pass, promises resolve and reject correctly, setTimeout works as expected

Reviewed By: javache

Differential Revision: D69059102

Pulled By: cipolleschi

fbshipit-source-id: d7de2c4adcf4dfc1d15e597e2a801e23c8d652aa
This commit is contained in:
kewde
2025-02-10 03:43:58 -08:00
committed by Facebook GitHub Bot
parent fd36a51456
commit 385dd59c0a
+6 -4
View File
@@ -36,6 +36,7 @@ const IDLE_CALLBACK_FRAME_DEADLINE = 1;
const callbacks: Array<?Function> = [];
const types: Array<?JSTimerType> = [];
const timerIDs: Array<?number> = [];
const freeIdxs: Array<number> = [];
let reactNativeMicrotasks: Array<number> = [];
let requestIdleCallbacks: Array<number> = [];
const requestIdleCallbackTimeouts: {[number]: number, ...} = {};
@@ -47,11 +48,11 @@ let hasEmittedTimeDriftWarning = false;
// Returns a free index if one is available, and the next consecutive index otherwise.
function _getFreeIndex(): number {
let freeIndex = timerIDs.indexOf(null);
if (freeIndex === -1) {
freeIndex = timerIDs.length;
const freeIdx = freeIdxs.pop();
if (freeIdx === undefined) {
return timerIDs.length;
}
return freeIndex;
return freeIdx;
}
function _allocateCallback(func: Function, type: JSTimerType): number {
@@ -171,6 +172,7 @@ function _clearIndex(i: number) {
timerIDs[i] = null;
callbacks[i] = null;
types[i] = null;
freeIdxs.push(i);
}
function _freeCallback(timerID: number) {