Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix laggy scrolling on mobile app's home screen, etc. #4108

Merged
merged 3 commits into from
May 21, 2024

Conversation

zetavg
Copy link
Contributor

@zetavg zetavg commented May 19, 2024

Problem

Scrolling through some screens (such as the feed on the Home screen) in the Bluesky app is laggy on some mid- to low-end Android devices, with frame rates dropping below 30 FPS.

While not having a noticeable impact on iOS or high-end Android devices, this situation will start escalating to a severe level on mid- or low-end Android devices, giving the user a bad experience or even causing them to decide to switch to the web version instead to avoid the laggy scroll hurting their eyes.

Notably, this issue does not affect every scrollable List, such as the one on the Profile screen.

The small change in this PR improves performance by 200% on some Android devices
(worst case <15 FPS → worst case >30 FPS).

See screen recording: https://youtu.be/ipZeTkb2mAk

Cause and Solution

In the List component (src/view/com/util/List.tsx), we have the following code:

  // ...

  const contextScrollHandlers = useScrollHandlers()

  // ...

  const scrollHandler = useAnimatedScrollHandler({
    onBeginDrag(e, ctx) {
      contextScrollHandlers.onBeginDrag?.(e, ctx)
    },
    onEndDrag(e, ctx) {
      contextScrollHandlers.onEndDrag?.(e, ctx)
    },
    onScroll(e, ctx) {
      contextScrollHandlers.onScroll?.(e, ctx)
    },
    onMomentumEnd(e, ctx) {
      contextScrollHandlers.onMomentumEnd?.(e, ctx)
    },
  })

  // ...

This might hurt performance since:

  • Grabbing .onScroll from the contextScrollHandlers object every time when onScroll is called may add influential computing overhead, as the handler may be called over 60 times per second.
  • Serializing the contextScrollHandlers object from JS thread and passing it to the UI thread on every call adds overhead due to additional serializing and deserializing.

(The above two points are assumptions, as I am unfamiliar with details on how worklet functions are serialized by reanimated or optimized by the JS runtime. Please correct me if I am wrong!)

(For the reason why this issue does not affect every List, I think it's because not every List is used with a onScroll event handler in the ScrollContext)

Solution

Destructure the contextScrollHandlers object beforehand to avoid repeated object access and unnecessary data transfer to the UI thread:

  // ...

  const contextScrollHandlers = useScrollHandlers()

  // ...

  const {
    onBeginDrag: onBeginDragFromContext,
    onEndDrag: onEndDragFromContext,
    onScroll: onScrollFromContext,
    onMomentumEnd: onMomentumEndFromContext,
  } = contextScrollHandlers

  const scrollHandler = useAnimatedScrollHandler({
    onBeginDrag(e, ctx) {
      onBeginDragFromContext?.(e, ctx)
    },
    onEndDrag(e, ctx) {
      onEndDragFromContext?.(e, ctx)
    },
    onScroll(e, ctx) {
      onScrollFromContext?.(e, ctx)
    },
    onMomentumEnd(e, ctx) {
      onMomentumEndFromContext?.(e, ctx)
    },
  })

  // ...

@gaearon gaearon merged commit cadc33c into bluesky-social:main May 21, 2024
6 checks passed
@gaearon
Copy link
Collaborator

gaearon commented May 21, 2024

Have not tried on a real device, but your video looks great and the change looks safe. Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants