as

Settings
Sign out
Notifications
Alexa
Amazonアプリストア
AWS
ドキュメント
Support
Contact Us
My Cases
開発
設計と開発
公開
リファレンス
サポート

JavaScriptスレッドのパフォーマンスの調査

JavaScriptスレッドのパフォーマンスの調査

スレッド状態の視覚化により、アプリのライフサイクル中のスレッドの状態を視覚化できます。これは、JavaScript(JS)アプリのパフォーマンスのトラブルシューティングと最適化に役立ちます。

スレッド状態の視覚化を効果的に使用するには、このページのガイダンスに従ってください。

スレッドの状態について

スレッドには、Running、Runnable、Runnable (Preempted)、Sleeping、Uninterruptible Sleep、Stopped、Idle、Exitなど、さまざまな状態があります。各状態の正確な定義は、アプリパフォーマンスのモニタリングと記録の「スレッドの状態」を参照してください。

パフォーマンスに関する問題の原因の特定

スレッド状態の視覚化によって、レイテンシの増加、応答不能、クラッシュ、一貫性のない動作など、アプリのパフォーマンスに関する問題を把握できます。次の表は、スレッド状態の情報を使用して特定できるパフォーマンスに関する問題の原因の一部を示しています。

原因 説明 関連するスレッドの状態
長時間実行中のスレッド スレッドが長期間「Running」状態のままである場合は、処理の効率が悪いか、操作が長時間実行されている可能性があり、最適化により改善される場合があります。 Running
CPU使用率が高い 多くのスレッドがアクティブに「Running」状態にある場合は、アルゴリズムの効率が悪いか、コンテキストが過剰に切り替わり、リソースに負荷がかかっている可能性があります。 Running、Runnable
CPUの競合 複数のスレッドが限られたCPUリソースをめぐって競合すると、高レベルの競合が発生します。これにより、スレッドがプリエンプトされたり、共有リソースへのアクセスを待機したりする可能性があります。 Runnable(Preempted)

Vega Studio Performance Extensionの使用

Vega Studio Performance Extension(VSPE)を使用すると、Activity MonitorやMemory MonitorのRecording Viewでスレッド状態を視覚化できます。JSアプリのパフォーマンスの問題をトラブルシューティングするには、次の手順を実行します。

  1. Activity Monitorで記録を開始します。
  2. 問題のある動作をアプリ内で再現します。
  3. 記録を停止してRecording Viewを生成します。これには、スレッド状態の情報を含むTracesビューが含まれます。

スレッド状態データの分析

アプリのパフォーマンスの問題を特定するには、次の手順を実行します。

  1. Tracesビューでスレッド状態データを分析します。
  2. スレッド状態のデータが、スレッドの実行時間が長い、CPU使用率が高い、CPUの競合など、前述の原因のいずれかに対応しているかどうかを調べます。

ユースケース

次の例は、スレッド状態の視覚化がアプリのパフォーマンスのトラブルシューティングと最適化にどのように役立つかを示しています。

ユースケース1: 非効率的なアルゴリズムが原因で実行時間が長いスレッドのトラブルシューティング

VegaVideoAppのテスト中に、ホーム画面や詳細ページを表示したときに、応答性が低下する問題が発生しました。

問題を特定するために、問題のある動作をアプリで再現しながら、Activity Monitorで記録を開始しました。記録を停止すると、Activity Monitorによって、スレッド状態の情報を示すTracesビューを含むRecording Viewが生成されました。Tracesを調べたところ、JSスレッドが最大5秒間実行されていたことがわかりました。これは、非効率的なアルゴリズムが原因でスレッドの実行時間が長くなっていることを示しています。

[Traces] ビューにJSスレッドが5秒間実行状態になっていることが示され、長時間実行されているスレッドのパフォーマンス上の問題が示される
スレッド状態の視覚化により、長時間実行されるJSスレッドの問題が明らかに

CPUプロファイラーのフレームグラフを調べたところ、getClassicsメソッドが原因でDetailsScreenの実行に5秒かかっていました。

getClassics メソッドの効率が悪いために5秒かかるDetailsScreenが強調表示されたCPUプロファイラーフレームグラフ
フレームグラフはgetClassicsメソッドをパフォーマンスのボトルネックとして特定

アプリのコードを見直してみると、データの並べ替えやフィルタリングを行う際のコードが非効率的であり、パフォーマンス遅延の問題が発生していることに気付きました。

 export const getClassics = (): TitleData[] => {
   const classics = [
     {
       id: '169327',
       title: 'Ethereal Echos',
       description: 'Lorem ipsum dolor sit amet',
       mediaType: 'video',
       mediaSourceType: 'url',
       duration: 300,
       thumbnail: tile09,
       posterUrl: tile09,
       videoUrl:
         'https://edge-vod-media.cdn01.net/encoded/0000169/0169313/video_1880k/T7J66Z106.mp4?source=firetv&channelID=13454',
       categories: ['Hits'],
       channelID: '13455',
       rating: '5',
       releaseDate: '09/20',
       format: 'MP4',
       uhd: false,
       secure: false,
       rentAmount: '200',
     },
     ...
     ...
   ];

   // 非効率的な並べ替え
   定数ソートクラシックス = (配列: TitleData[]): TitleData[] => {
     const n = array.length;
     for (let i = 0; i < n; i++) {
       for (let j = 0; j < n - i - 1; j++) {
         for (let k = 0; k < n; k++) {
           if (array[k].title === 'NA') {
             continue;
           }
         }

         if (array[j].title.localeCompare(array[j + 1].title) > 0) {
           // 現在の要素が次の要素より大きい場合はスワップ
           const temp = array[j];
           array[j] = array[j + 1];
           array[j + 1] = temp;
         }
       }
     }
     return array;
   };

   // 入れ子になったループを使用した非効率的なフィルタリング
   const filterClassics = (array: TitleData[], category: string): TitleData[] => {
     const result: TitleData[] = [];

     for (let i = 0; i < array.length; i++) {
       let found = false;
       for (let j = 0; j < array.length; j++) {
         if (array[i].categories.includes(category) && array[i] === array[j]) {
           found = true;
           break; 
         }
       }
     }

     return result;
   };

   const sortedClassics = sortClassics(classics);

   const filteredClassics = filterClassics(sortedClassics, 'Hits');

   return filteredClassics;
 };

この問題を解決するには、不要な入れ子になったループを削除します。次に、Tracesビューに再度アクセスして、アプリのパフォーマンスが向上するかどうかを確認します。

コード最適化後にJSスレッドの実行時間が短縮されたことでパフォーマンスが向上したことを示す [Traces] ビュー
非効率的なコードを削除すると、スレッドの状態でパフォーマンスが向上

ユースケース2: スクロールアプリのCPU使用率が高い場合のトラブルシューティング

React Native Flatlistコンポーネントを使用して、200行を異なる色で表示するシンプルなスクロールアプリを作成しました。Activity Monitorで確認すると、スクロール中にThread State Viewに「Running」の状態が頻繁に表示され、CPU使用率が高いことが示されました。CPUプロファイラーのフレームグラフでも高いアクティビティが示されました。これは、Flatlistコンポーネントが効率的に機能していないことを示唆しています。

FlatListのスクロール中に頻繁に実行されている状態と高いアクティビティを示すスレッド状態の視覚化とCPUプロファイラー
FlatListのスクロールパフォーマンス中に高いCPU使用率を検出

次のコード例は、Flatlistコンポーネントがアプリにどのように追加されたかを示しています。

 const PerfFlatListApp = () => {
     // https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem
     const renderItem = useCallback(
         ({ item, index }: { item: FlatListItem; index: number }) => (
             // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-basic-components
             <Item
                 key={item.title}
                 buttonStyle={[
                     styles.item,
                     {
                         backgroundColor: calculateBackgroundColor(index)
                     }
                 ]}
                 hasTVPreferredFocus={item.id === 0}
             />
         ),
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-keyextractor-or-key
     const keyExtractor = useCallback(
         (item: FlatListItem, index: number) => item.title,
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-getitemlayout
     const getItemLayout = useCallback(
         (
             _data: any,
             index: number
         ): { length: number; offset: number; index: number } => ({
             index,
             length: ITEM_HEIGHT,
             offset: ITEM_HEIGHT * index
         }),
         []
     );

     useEffect(() => {
         console.log('reporting fully_drawn');
         // @ts-ignore
         global.performance.reportFullyDrawn();
     }, []);

     return (
         <View style={styles.container}>
             <FlatList
                 data={data}
                 renderItem={renderItem}
                 // https://reactnative.dev/docs/optimizing-flatlist-configuration#initialnumtorender
                 // 明示的に10に設定しているのは、画面に表示できる項目数の初期値であるためです
                 initialNumToRender={10}
                 keyExtractor={keyExtractor}
                 getItemLayout={getItemLayout}
                 // https://reactnative.dev/docs/optimizing-flatlist-configuration#windowsize,
                 // TVプロファイルよりもMMでのウィンドウサイズを大きく設定しているのは、クイックtouch
                 // スワイプした場合、D-Padを長押しするよりもフラットリストをすばやくスクロールできるためです。ウィンドウサイズが
                 // 大きいほどより多くのメモリを消費しますが、スクロール時に表示される空白領域は少なくなります。
                 windowSize={Platform.isTV ? 5 : 13}
             />
         </View>
     );
 };

このパフォーマンスの問題は、Flatlistコンポーネントを、スクロールシナリオのパフォーマンスを向上させるように設計された、ShopifyのFlashlistコンポーネントに置き換えます。

クリップボードにコピーしました。

 const PerfFlashListApp = () => {
     // https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem
     const renderItem = useCallback(
         ({ item, index }: { item: FlashListItem; index: number }) => (
             // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-basic-components
             <Item
                 buttonStyle={[
                     styles.item,
                     {
                         backgroundColor: calculateBackgroundColor(index)
                     }
                 ]}
                 hasTVPreferredFocus={item.id === 0}
             />
         ),
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-keyextractor-or-key
     const keyExtractor = useCallback(
         (item: FlashListItem, index: number) => item.title,
         []
     );

     useEffect(() => {
         console.log('reporting fully_drawn');
         // @ts-ignore
         global.performance.reportFullyDrawn();
     }, []);

     return (
         <View style={styles.container}>
             <FlashList
                 estimatedItemSize={ITEM_HEIGHT}
                 data={data}
                 renderItem={renderItem}
                 keyExtractor={keyExtractor}
             />
         </View>
     );
 };

アプリのパフォーマンスを再テストし、Thread State Viewの「Running」状態の数が減少しているかどうかを確認します。CPUプロファイラーのフレームグラフにも、アクティビティの減少が示されます。

FlatListをFlashListに置き換えた後、実行中の状態が減少し、CPUアクティビティが減少したことを示すスレッド状態の視覚化
FlashListコンポーネントに切り替えることでパフォーマンスが向上した

FlashlistがFlatlistより優れている点については、ベストプラクティスを参照してください。



Last updated: 2025年9月30日