Closed41

"How React 18 Improves Application Performance"を読む

hajimismhajimism

Main thread and Long Tasks

  • JSはシングルスレッド
  • 実行に50ミリ秒以上かかるタスクは"long task"

This 50ms benchmark is based on the fact that devices must create a new frame every 16ms (60fps) to maintain a smooth visual experience.

らしい。

The 50ms benchmark allows the device to allocate resources to both rendering frames and performing other tasks, and provides an additional ~33.33ms for the device to perform other tasks while maintaining a smooth visual experience.

よくわからない...

hajimismhajimism

パフォーマンス計測に重要な2つの指標、"Total Blocking Time, and Interaction to Next Paint."

hajimismhajimism

To understand how the new React updates optimize for these measurements and thus improve the user experience, it's important to understand how traditional React works first.

これらの指標をSuspenseたちがどう改善していくかのStoryらしい、面白そう

hajimismhajimism

パフォーマンス計測の話がよく理解できてないかもなので、linkされていた記事を読む

hajimismhajimism

What are long tasks?

https://web.dev/long-tasks-devtools/#what-are-long-tasks

  • long taskとは、メインスレッドを長時間独占してUIをフリーズさせる処理
  • RAILモデルでは、100ms以内に目に見えるレスポンスを確実に返すために、50msでユーザー入力イベントを処理することを推奨している
  • そうしないと、アクションとリアクションの間の接続が途切れる

Chrome Devtoolはlong taskを可視化できるらしい。
https://developer.chrome.com/blog/new-in-devtools-74/#longtasks

hajimismhajimism

long taskの王道の対処法は、短いチャンクに処理を分割することらしい。のちの説明に効いてきそうなポイント。

Break all your work into small chunks (that run in < 50ms) and run these chunks at the right place and time; the right place may even be off the main thread, in a worker. Phil Walton's Idle Until Urgent is a good read on this topic. See also the optimize long tasks article for general strategies for managing and breaking up long tasks.

hajimismhajimism

What is RAIL model?

https://web.dev/rail/

RAIL is a user-centric performance model that provides a structure for thinking about performance. The model breaks down the user's experience into key actions (for example, tap, scroll, load) and helps you define performance goals for each of them.

冒頭ですでに魅力的に見える。

What RAIL stands for

hajimismhajimism

レスポンスの時間とユーザーの体感が表になっていて面白い。

0.1秒くらいで動けばサクサクで、1秒かかると遅い

hajimismhajimism

重要なガイドライン

  • To ensure a visible response within 100 ms, process user input events within 50 ms. This applies to most inputs, such as clicking buttons, toggling form controls, or starting animations. This does not apply to touch drags or scrolls.
  • Though it may sound counterintuitive, it's not always the right call to respond to user input immediately. You can use this 100 ms window to do other expensive work, but be careful not to block the user. If possible, do work in the background.
  • For actions that take longer than 50 ms to complete, always provide feedback.

最初がuseTransitionっぽくて、最後がSuspenseっぽい

hajimismhajimism

100ミリ秒以内に反応することが目標だが、そのうちの50ミリ秒は他のタスクに取られてしまうことがあるため、入力処理に使える時間は実際には残りの50ミリ秒だけ

hajimismhajimism

Traditional React Rendering

A visual update in React is divided into two phases: the render phase and the commit phase.

進研ゼミでやったところだ!
https://zenn.dev/link/comments/7ce8f59054f7ac

In a traditional synchronous render, React would give the same priority to all elements within a component tree.

React would go ahead and render the tree in a single uninterruptible task,

A synchronous render is an “all-or-nothing” operation, where it’s guaranteed that a component that starts rendering will always finish.

従来のrenderingだと、"a single uninterruptible task" = “all-or-nothing” なので、long taskになりがちでしたよと。

hajimismhajimism

提供されているDemoは"there’s a clear visual feedback delay"らしいのだが、サクサク動くのでわからない。ハイスペックなPC使ってると、「自分の環境ではサクサクだけどもユーザーの環境ではイマイチ」っていうことが往々にしてありそうなので、やはりパフォーマンス計測の仕組みを作らないとだな...。

hajimismhajimism

6x遅くして手元で計測してみたけれども、long taskってこういうこと?イマイチ使い方わかってない

hajimismhajimism

React 18 introduces a new concurrent renderer that operates behind the scenes. This renderer exposes some ways for us to mark certain renders as non-urgent.

a traditional synchronous renderとa new concurrent renderを対比させ、その差はpriorityの付け方にあると言っている。

hajimismhajimism

synchronousとconcurrentの感覚の差がわからない

synchronous | ˈsiNGkrənəs |

adjective
1 existing or occurring at the same time: glaciations were approximately synchronous in both hemispheres.
2 Astronomy (of a satellite or its orbit) making or denoting an orbit around the earth or another celestial body in which one revolution is completed in the period taken for the body to rotate about its axis.

concurrent | kənˈkərənt |

adjective
existing, happening, or done at the same time: there are three concurrent art fairs around the city.
• (of two or more prison sentences) to be served at the same time.
• Mathematics (of three or more lines) meeting at or tending toward one point.

hajimismhajimism

Both concurrent and synchronous refer to different types of processing in computing, but they each have a distinct meaning:

Concurrent Processing: Concurrent programming or processing refers to the ability of a system to execute multiple tasks or processes in overlapping time intervals. This doesn't necessarily mean that tasks are being performed at the exact same instant. Instead, they can be interweaved, with different tasks making progress over time. In concurrent systems, individual tasks often interact with each other and need to be coordinated in some way, such as through locks, semaphores, or other synchronization mechanisms. Concurrent execution can happen on single or multiple cores. The purpose of concurrency is to model interactions in a system and to increase the efficiency and responsiveness of software.

Synchronous Processing: Synchronous operations are those where tasks are performed in sequence, one after another. This means that a task must wait for the previous one to complete before it can start. In the context of network operations or I/O, synchronous also means that the system waits for the operation to complete before continuing with the rest of the program. This can cause blocking, which can reduce efficiency if the task being waited on takes a long time. The advantage of synchronous programming is that it's generally simpler to understand and reason about, because you don't have to worry about the complications of tasks interacting with each other in complex ways, as you do in concurrent or asynchronous programming.

synchronousが同期処理でconcurrentが並行処理っぽい(ChatGPTより)

hajimismhajimism

In that case, React will yield back to the main thread every 5 milliseconds to see if there are more important tasks to handle instead,

この事実はReactのどこを見ればわかるのだろう

hajimismhajimism

the concurrent renderer yields control back to the main thread at intervals of 5ms during the (re)rendering of low-priority components.

いやー、字面ではわかるんだけどこの感覚つかめない。計算機に対する感覚が足りてない...。

hajimismhajimism

Additionally, the concurrent renderer is able to “concurrently” render multiple versions of the component tree in the background without immediately committing the result.

all-or-nothingの1点集中ではなくて、並行処理ができる...というより、「優先度の低いものを一時停止しておける」の方が実態に近いのだろうな。

hajimismhajimism

Transitions

We can mark an update as non-urgent by using the startTransition function made available by the useTransition hook. This is a powerful new feature that allows us to mark certain state updates as “transitions”, indicating that they can lead to visual changes that could potentially disrupt user experience if they were rendered synchronously.

"indicating"のところから、transitionに関する明晰な説明。「synchronouslyに処理したらUXを損なうだろう」という意味付けであるという説明が、ここまでの文脈ときれいに接続している。

By wrapping a state update in startTransition, we can tell React that we’re okay with deferring or interrupting the rendering to prioritize more important tasks to keep the current user interface interactive.

"deferring"という言葉遣いは明らかにuseDeferredValueと同じ文脈から来ている。こういうのほんとはPRとか追いかけられるといいんだろうな。

hajimismhajimism

下記のコードにおいて、setSearchQuery(e.target.value)がtransitionとしてマークされている。もしこれがsynchronouslyに処理されたならば、CityListの検索がinputのonChangeごとに発火してしまい、その処理が重いのでinput操作がカクついてしまう。

transtionとしてマークされていることで、setSearchQuery(e.target.value)はdeferできる。ユーザーがinputしている間はそちらのレンダリングを優先して行い、そちらのほとぼりが冷めてからtranstionを処理してcommitする。だからカクつかない。

export default function SearchCities() {
  const [text, setText] = useState("Am");
  const [searchQuery, setSearchQuery] = useState(text);
  const [isPending, startTransition] = useTransition();

   return (    
      <main>      
          <h1><code>startTransition</code></h1>      
          <input  
              type="text" 
              value={text}
              onChange={(e) => {
                 setText(e.target.value)
                 startTransition(() => {
                    setSearchQuery(e.target.value)
                 })
             }}  />      
          <CityList searchQuery={searchQuery} />    
      </main>  
     );
};
hajimismhajimism

React Server Components

CSRやSSRでは、JSバンドルをまるごとClientに送る必要があった。しかしRSCは、シリアライズされたコンポーネントツリーをクライアントに送るのでJSバンドルをサーバーに置いておける。

これSSRのときはなんでこうしなかったのかよくわかってないんだよな...。Streamingを考えてなかったから?

hajimismhajimism

This tells the bundler to add this component and its imports to the client bundle and tells React to hydrate the tree client-side to add interactivity.

'use client'でマークすることで、そのファイルとファイルの中のimportをclient bundleとする

hajimismhajimism

地味に重要なポイント

Note: Framework implementations may differ. For example, Next.js will prerender Client Components to HTML on the server, similar to the traditional SSR approach. By default, however, Client Components are rendered similar to the CSR approach.

hajimismhajimism

Ensuring that only the leaf-most node of the interactive component defines the "use client" directive. This may require some component decoupling.

コンポーネントの分割って split componentやとおもってたけれども、ネイティブはcomponent decouplingと呼ぶらしい。

hajimismhajimism

Suspense

サーバーサイドの人はこの感じで直接DB触りたくないって言ってた。わかるけどそれは抽象化の度合いで解決でき、本質的な問題ではないだろう。

async function BlogPosts() {
  const posts = await db.posts.findAll();
  return '...';
}
 
export default function Page() {
  return (
    <Suspense fallback={<Skeleton />}>
      <BlogPosts />
    </Suspense>
  )
}
hajimismhajimism

When a component is suspended, for example because it’s still waiting for data to load, React doesn't just sit idle until the component has received the data. Instead, it pauses the rendering of the suspended component and shifts its focus to other tasks.

Suspendはただのidlingではなく、「優先度下げ」のマーキングでもあるということ、でいいのかな

hajimismhajimism

React can also reprioritize components based on user interaction. For example, when a user interacts with a suspended component that's not currently being rendered, React suspends the ongoing render and prioritizes the component that the user is interacting with.

ほんまか?てか"user interacts with a suspended component that's not currently being rendered"てどういう状況?具体例がほしい。

hajimismhajimism

まあいずれにせよSuspense大好きなので、「そうであったら嬉しい」で終わりではある。

hajimismhajimism

読んでみて

めちゃくちゃ良かったな。特にSync vs Concを理解できてよかった。

自分への課題としては

  • パフォーマンス計測の仕方
  • 開発者ツールの使い方
  • そもそもStreamingってどうなってるの

っていう基礎的なところとか、react.devでuseTransitionやuseDeferredValueのDocsを読むとかが挙げられる。

hajimismhajimism

Concをもうちょい自分の言葉で説明できるようにいまのうちにuseTransitionとかのDocsを見ておきたい気持ちのがいちばん強いかも。

「5msごとにMain threadにcontrolを戻す」あたりとか、公式docsにどこまで書いているんだろう。LearnのところにConcの話は無かった気もするが...

hajimismhajimism

いやー、字面ではわかるんだけどこの感覚つかめない。計算機に対する感覚が足りてない...。

あとはこの深刻な問題も...

このスクラップは2023/07/31にクローズされました