Skip to content

Commit f503b21

Browse files
Almourofacebook-github-bot
authored andcommitted
improve interpolation performance with big input range (#33598)
Summary: This drastically improves `Animated.interpolate` performance when `inputRange` has a considerable amount of elements (~100 in my tests). For instance in `ActivityIndicator` inside `react-native-paper`, the input has 144 elements https://github.com/callstack/react-native-paper/blob/main/src/components/ActivityIndicator.tsx#L170. `react-native-elements` has 9k stars, so I'm assuming this is widely used. ### Cause The reason for the performance drop is that if we assume `n` to be the size of the range, calculating `'inputRange must be monotonically non-decreasing ' + arr` essentially calculates `arr.toString()` which has O(n) complexity. Since it is recalculated in a for loop, we end up with `checkValidInputRange` having a O(n²) complexity. Which means ~10k operations if the array has a size close to 100. ## Changelog [General] [Fixed] - Fix performance issue on Animated.interpolate with big input range Pull Request resolved: #33598 Test Plan: [Here's a repo](https://github.com/Almouro/AnimatedInterpolationRepro) reproducing the issue. The branch `fix` includes the fix. Clicking `Interpolate` runs: ```js new Animated.Value(0).interpolate({ inputRange: Array(144) .fill() .map((_, i) => 1 / (i + 1)) .reverse(), outputRange: Array(144) .fill() .map((_, i) => 1 / (i + 1)) ``` Here's a comparison of JS thread perf before the fix and after the fix: - on a Samsung J3 2017 (lower end) - using Flipper and https://github.com/bamlab/react-native-performance) - ` __DEV__` mode deactivated - clicking the button and waiting 15s | Before | After | |----------|:-------------:| | ![image](https://user-images.githubusercontent.com/4534323/162413692-307c2be1-5c7f-4e7f-ba69-8ba8d7c52bda.png) | ![image](https://user-images.githubusercontent.com/4534323/162413842-780f12d2-ce8b-457c-b66c-c6d86f14ed28.png)| The error still throws if `inputRange` is incorrect: <img width="517" alt="image" src="https://user-images.githubusercontent.com/4534323/162439219-6ce120ae-98e5-496b-899a-492978689d6d.png"> However if `__DEV__` mode is deactivated, no error is thrown Reviewed By: yungsters Differential Revision: D35507441 Pulled By: javache fbshipit-source-id: 36ac49422f7a42d247130c42d12248b2be1232c6
1 parent e147365 commit f503b21

File tree

1 file changed

+17
-20
lines changed

1 file changed

+17
-20
lines changed

Libraries/Animated/nodes/AnimatedInterpolation.js

+17-20
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,23 @@ function createInterpolation(
4646
}
4747

4848
const outputRange: Array<number> = (config.outputRange: any);
49-
checkInfiniteRange('outputRange', outputRange);
5049

5150
const inputRange = config.inputRange;
52-
checkInfiniteRange('inputRange', inputRange);
53-
checkValidInputRange(inputRange);
5451

55-
invariant(
56-
inputRange.length === outputRange.length,
57-
'inputRange (' +
58-
inputRange.length +
59-
') and outputRange (' +
60-
outputRange.length +
61-
') must have the same length',
62-
);
52+
if (__DEV__) {
53+
checkInfiniteRange('outputRange', outputRange);
54+
checkInfiniteRange('inputRange', inputRange);
55+
checkValidInputRange(inputRange);
56+
57+
invariant(
58+
inputRange.length === outputRange.length,
59+
'inputRange (' +
60+
inputRange.length +
61+
') and outputRange (' +
62+
outputRange.length +
63+
') must have the same length',
64+
);
65+
}
6366

6467
const easing = config.easing || linear;
6568

@@ -276,16 +279,10 @@ function findRange(input: number, inputRange: $ReadOnlyArray<number>) {
276279

277280
function checkValidInputRange(arr: $ReadOnlyArray<number>) {
278281
invariant(arr.length >= 2, 'inputRange must have at least 2 elements');
282+
const message =
283+
'inputRange must be monotonically non-decreasing ' + String(arr);
279284
for (let i = 1; i < arr.length; ++i) {
280-
invariant(
281-
arr[i] >= arr[i - 1],
282-
/* $FlowFixMe[incompatible-type] (>=0.13.0) - In the addition expression
283-
* below this comment, one or both of the operands may be something that
284-
* doesn't cleanly convert to a string, like undefined, null, and object,
285-
* etc. If you really mean this implicit string conversion, you can do
286-
* something like String(myThing) */
287-
'inputRange must be monotonically non-decreasing ' + arr,
288-
);
285+
invariant(arr[i] >= arr[i - 1], message);
289286
}
290287
}
291288

0 commit comments

Comments
 (0)