ユニバーサル Windows アプリ (UWP) での CPU 使用率を分析しますAnalyze CPU Usage in a Universal Windows App (UWP)

Windows と Windows Phone に適用Applies to Windows and Windows Phone

アプリのパフォーマンスの問題を調査する必要がある場合、まず CPU の使用状況を理解することから始めることができます。When you need to investigate performance issues in your app, a good place to start is understanding how it uses the CPU. CPU 使用率 ツールは、CPU がコードを実行する際、どこで時間を費やしているかを示します。The CPU Usage tool shows you where the CPU is spending time executing code. 特定のシナリオに限定する場合、単一の診断セッションで、CPU 使用率を Application Timeline ツール、エネルギー消費ツール、またはその両方と一緒に実行できます。To focus on specific scenarios, CPU Usage can be run with the Application Timeline tool, the Energy Consumption tool, or both tools in a single diagnostic session.

注意

CPU 使用率ツールは、Windows Phone Silverlight 8.1 アプリには使用できません。The CPU Usage tool cannot be used with Windows Phone Silverlight 8.1 apps.

このチュートリアルでは、簡単な Windows Universal XAML アプリの CPU 使用率の収集と分析について説明します。This walkthrough takes you through collecting and analyzing CPU usage for a simple Windows Universal XAML app.

CpuUseDemo プロジェクトの作成Create the CpuUseDemo project

CpuUseDemo とは、CPU 使用率のデータを収集して分析する方法を説明するために作成されたアプリです。CpuUseDemo is an app that was created to demonstrate how to collect and analyze CPU usage data. ボタンは、関数に対する複数の呼び出しの中から最大の値を選択するメソッドを呼び出して数字を生成します。The buttons generate a number by calling a method that selects the maximum value from multiple calls to a function. 呼び出された関数は、非常に多くの乱数値を作成した後、最後の値を返します。The called function creates a very large number of random values and then returns the last one. データはテキスト ボックスに表示されます。The data is displayed in a text box.

  1. BlankApp テンプレートを使って CpuUseDemo という名前の新しい C# Windows Universal アプリ プロジェクトを作成します。Create a new C# Windows Universal app project named CpuUseDemo using the BlankApp template.

    CpuUseDemoProject の作成Create the CpuUseDemoProject

  2. MainPage.xaml をこのコードで置き換えます。Replace MainPage.xaml with this code.

  3. MainPage.xaml.cs をこのコードで置き換えます。Replace MainPage.xaml.cs with this code.

  4. アプリを構築してお試しください。アプリは、CPU 使用率データの分析の一般的なケースを表示するには十分です。Build the app and try it out. The app is simple enough to show you some common cases of CPU Usage data analysis.

CPU 使用率のデータの収集Collect CPU usage data

シミュレーターでアプリのリリース ビルドを実行するRun a release build of the app in the simulator

  1. Visual Studio で、配置ターゲットを [シミュレーター] に設定し、ソリューション構成を [リリース] に設定します。In Visual Studio, set the deployment target to Simulator and the solution configuration to Release.

    • シミュレーターでアプリを実行すると、アプリと Visual Studio IDE との間で簡単に切り替えることができます。Running the app in the simulator lets you switch easily between the app and the Visual Studio IDE.

    • このアプリを [リリース] モードで実行すると、アプリの実際のパフォーマンスをよりよく把握できます。Running this app in Release mode gives you a better view of the actual performance of your app.

  2. [デバッグ] メニューの [パフォーマンス プロファイラー...]をクリックします。On the Debug menu, choose Performance Profiler....

  3. パフォーマンスと診断ハブで、[CPU 使用率] をクリックしてから、[開始] をクリックします。In the Performance and Diagnostic hub, choose CPU Usage and then choose Start.

    CpuUsage 診断セッションの開始Start the CpuUsage diagnostic session

  4. アプリが起動したら、[最大数を取得] をクリックします。When the app starts, click Get Max Number. 出力の表示後に約 1 秒待ってから、[非同期の最大数の取得] を選択します。Wait about a second after the output is displayed, then choose Get Max Number Async. ボタンのクリック間隔を空けると、診断レポートにおいてボタン クリックのルーチンを分離しやすくなります。Waiting between button clicks makes it easier to isolate the button click routines in the diagnostic report.

  5. 2 番目の出力行が表示された後、パフォーマンスと診断ハブで [コレクションの停止] をクリックします。After the second output line appears, choose Stop Collection in the Performance and Diagnostic hub.

    CpuUsage データ コレクションの停止Stop CpuUsage data collection

    CPU 使用率ツールがデータを分析してレポートを表示します。The CPU Usage tool analyzes the data and displays the report.

    CpuUsage レポートCpuUsage report

CPU 使用率レポートの分析Analyze the CPU Usage report

CPU 使用状況タイムライン グラフCPU utilization timeline graph

CpuUtilization (%) のタイムライン グラフCpuUtilization (%) timeline graph

CPU 使用状況グラフは、デバイスにあるすべてのプロセッサー コアのすべての CPU 時間に対するアプリの CPU アクティビティの割合を示します。The CPU utilization graph shows the CPU activity of the app as a percent of all CPU time from all the processor cores on the device. このレポートのデータはデュアル コアのコンピューターで収集されました。The data of this report was collected on a dual-core machine. 2 つの急激な増加は、2 つのボタン クリックの CPU アクティビティを表します。The two large spikes represent the CPU activity of the two button clicks. GetMaxNumberButton_Click はシングル コアで同期的に実行されるため、メソッドのグラフの高さが 50% を超えていないのは理にかなっています。GetMaxNumberButton_Click performs synchronously on a single core, so that it makes sense that method's graph height never exceeds 50%. また、GetMaxNumberAsycButton_Click は非同期的に両方のコアで実行されるため、グラフの急激な増加が両方のコアでほぼすべての CPU リソースを活用しているように見えるのも適切と言えます。GetMaxNumberAsycButton_Click runs asynchronously across both cores, so it so it again looks right that its spike gets closer to utilizing all of the CPU resources on both cores.

詳細を確認するためにタイムライン セグメントを選択するSelect timeline segments to view details

[診断セッション] タイムラインの選択バーを使用して、GetMaxNumberButton_Click のデータに注目します。Use the selection bars on the Diagnostic session timeline to focus on the GetMaxNumberButton_Click data:

選択された GetMaxNumberButton_ClickGetMaxNumberButton_Click selected

[診断セッション] タイムラインでは、選択したセグメントで使用した時間 (このレポートでは 2 秒余り) が表示され、選択で実行されたメソッドを対象にコール ツリーをフィルターします。The Diagnostic session timeline now displays the time spent in the selected segment (a bit more than 2 seconds in this report) and filters the call tree to those methods that ran in the selection.

次に GetMaxNumberAsyncButton_Click セグメントを選択します。Now select the GetMaxNumberAsyncButton_Click segment.

GetMaxNumberAsyncButton_Click のレポート選択GetMaxNumberAsyncButton_Click report selection

このメソッドは GetMaxNumberButton_Click よりも 1 秒早く完了しますが、コール ツリー エントリの意味はそれほど明確ではありません。This method completes about a second faster than GetMaxNumberButton_Click, but the meaning of the call tree entries are less obvious.

CPU 使用率コール ツリーThe CPU Usage call tree

コール ツリー情報を理解するには、 GetMaxNumberButton_Click セグメントを再度選択し、コール ツリーの詳細を確認します。To get started understanding call tree information, reselect the GetMaxNumberButton_Click segment, and look at the call tree details.

コール ツリーの構造Call tree structure

GetMaxNumberButton_Click コール ツリーGetMaxNumberButton_Click call tree

手順 1Step 1 CPU 使用率コール ツリーのトップ レベルのノードは擬似ノードです。The top-level node in CPU Usage call trees is a pseudo-node
手順 2Step 2 ほとんどのアプリでは、 [外部コードの表示] オプションをオフにすると、セカンド レベルのノードは [外部コード] ノードとなります。このノードに含まれるシステムおよびフレームワーク コードは、アプリの開始と停止、UI の描画、スレッド スケジュールの制御、およびアプリへの他の低レベル サービスの提供を行います。In most apps, when the Show External Code option is disabled, the second-level node is an [External Code] node that contains the system and framework code that starts and stops the app, draws the UI, controls thread scheduling, and provides other low-level services to the app.
手順 3Step 3 セカンド レベル ノードの子はユーザー コード メソッドおよび非同期ルーチンで、セカンド レベル システムとフレームワーク コードによって呼び出される、または作成されます。The children of the second-level node are the user-code methods and asynchronous routines that are called or created by the second-level system and framework code.
手順 4Step 4 メソッドの子ノードには、親メソッドの呼び出しのみのデータが含まれます。Child nodes of a method contain data only for the calls of the parent method. [外部コードの表示] がオフのとき、アプリ メソッドには [外部コード] ノードが含まれる場合もあります。When Show External Code is disabled, app methods can also contain an [External Code] node.

外部コードExternal Code

外部コードは、作成したコードによって実行されるシステムおよびフレームワーク コンポーネント内の関数で構成されます。External code consists of functions in system and framework components that are executed by the code you write. 外部コードには、アプリの開始と停止、UI の描画、スレッドの制御、およびアプリへの他の低レベル サービスの提供を行う関数が含まれます。External code includes functions that start and stop the app, draw the UI, control threading, and provide other low-level services to the app. 外部コードを確認することはほとんどないため、CPU 使用率コール ツリーはユーザー メソッドの外部関数を 1 つの [外部コード] ノードにまとめます。In most cases, you won't be interested in external code, and so the CPU Usage call tree gathers the external functions of a user method into one [External Code] node.

外部コードのコール パスを表示する場合、 [フィルター ビュー] リストから [外部コードの表示] をクリックし、 [適用]をクリックします。When you want to view the call paths of external code, choose Show External Code from the Filter view list and then choose Apply.

[フィルター表示]、[外部コードの表示] の順に選択Choose Filter View, then Show External Code

多くの外部コードの呼び出しチェーンは複雑な入れ子になっているため、関数名列の幅は、一部の大型コンピューター モニターを除いてディスプレイの幅に収まりきらない可能性があります。Be aware that many external code call chains are deeply nested, so that the width of the Function Name column can exceed the display width of all but the largest of computer monitors. その場合、関数名は [...] と表示されます。When this happens, function names are shown as [...]:

コール ツリーの入れ子式の外部コードNested external code in the call tree

検索ボックスを使って目的のノードを探した後、水平スクロール バーを使ってデータを表示させます。Use the search box to find a node that you are looking for, then use the horizontal scroll bar to bring the data into view:

入れ子式の外部コードの検索Search for nested external code

コール ツリー データの列Call tree data columns

合計 CPU (%)Total CPU (%) 合計 % のデータ演算式Total % data equation

選択した時間範囲におけるアプリの CPU アクティビティのうち、関数の呼び出し、および関数が呼び出した関数が使用した割合です。The percentage of the app's CPU activity in the selected time range that was used by calls to the function and the functions called by the function. これは、特定の時間範囲におけるアプリの合計アクティビティと、利用可能な合計 CPU 能力とを比較する [CPU 使用率] タイムライン グラフとは異なります。Note that this is different from the CPU Utilization timeline graph, which compares the total activity of the app in a time range to the total available CPU capacity.
セルフ CPU (%)Self CPU (%) 自己 % 演算式Self % equation

選択した時間範囲におけるアプリの CPU アクティビティのうち、関数の呼び出しが使用した割合です。ただし、関数から呼び出された関数のアクティビティは除きます。The percentage of the app's CPU activity in the selected time range that was used by the calls to the function, excluding the activity of functions called by the function.
合計 CPU (ミリ秒)Total CPU (ms) 選択した時間範囲内で、関数への呼び出しおよび関数が呼び出した関数によって使用されたミリ秒です。The number of milliseconds spent in calls to the function in the selected time range and the functions that were called by the function.
セルフ CPU (ミリ秒)Self CPU (ms) 選択した時間範囲内で、関数への呼び出しおよび関数が呼び出した関数によって使用されたミリ秒です。The number of milliseconds spent in calls to the function in the selected time range and the functions that were called by the function.
モジュールModule 関数が含まれるモジュールの名前です。あるいは、[外部コード] ノード内の関数が含まれるモジュールの数です。The name of the module containing the function, or the number of modules containing the functions in an [External Code] node.

CPU 使用率コール ツリー内の非同期関数Asynchronous functions in the CPU Usage call tree

コンパイラが非同期メソッドを検出すると、メソッドの実行を制御するために非表示のクラスを作成します。When the compiler encounters an asynchronous method, it creates a hidden class to control the method's execution. 概念的に言って、クラスとはコンパイラによって生成された関数のリストを含んだステート マシンです。これらの関数は、元のメソッドの操作を非同期で呼び出したり、それに必要なコールバック、スケジューラ、および反復子を正しく呼び出したりします。Conceptually, the class is a state machine that includes a list of compiler-generated functions that call operations of the original method asynchronously, and the callbacks, scheduler, and iterators required to them correctly. 元のメソッドが親メソッドによって呼び出されると、ランタイムは親の実行コンテキストからメソッドを削除し、アプリの実行を制御するシステムとフレームワーク コードのコンテキストにある非表示のクラスのメソッドを実行します。When the original method is called by a parent method, the runtime removes the method from the execution context of the parent, and runs the methods of the hidden class in the context of the system and framework code that control the app's execution. 非同期のメソッドは、多くの場合、1 つ以上の異なるスレッドで実行されます (必ずそうなるわけではありません)。The asynchronous methods are often, but not always, executed on one or more different threads. このコードは、CPU 使用率コール ツリーで、ツリーのトップ ノードのすぐ下にある [外部コード] ノードの子として表示されます。This code is shown in the CPU Usage call tree as children of the [External Code] node immediately below the top node of the tree.

これをこの例で表示するには、タイムラインで GetMaxNumberAsyncButton_Click セグメントを再度選択します。To see this in our example, re-select the GetMaxNumberAsyncButton_Click segment in the timeline.

GetMaxNumberAsyncButton_Click のレポート選択GetMaxNumberAsyncButton_Click report selection

[外部コード] の下にある最初の 2 つのノードは、ステート マシン クラスのコンパイラ生成メソッドです。The first two nodes under [External Code] are the compiler-generated methods of the state machine class. 3 つ目は、元のメソッドへの呼び出しです。The third is the call to original method. 生成されたメソッドを展開すると詳細が表示されます。Expanding the generated methods shows you what's going on.

展開された GetMaxNumberAsyncButton_Click コール ツリーExpanded GetMaxNumberAsyncButton_Click call tree

  • MainPage::GetMaxNumberAsyncButton_Click の機能は非常にわずかで、タスクの値のリストを管理し、結果の最大値を計算し、出力を表示するに過ぎません。MainPage::GetMaxNumberAsyncButton_Click does very little; it manages a list of the task values, computes the maximum of the results, and displays the output.

  • MainPage+<GetMaxNumberAsyncButton_Click>d__3::MoveNext は、 GetNumberAsyncの呼び出しをラップする 48 個のタスクをスケジュールして起動するために必要なアクティビティを表示します。MainPage+<GetMaxNumberAsyncButton_Click>d__3::MoveNext shows you the activity required to schedule and launch the 48 tasks that wrap the call to GetNumberAsync.

  • MainPage::<GetNumberAsync>b__bGetNumber を呼び出すタスクのアクティビティを表示します。MainPage::<GetNumberAsync>b__b shows you the activity of the tasks that call GetNumber.

次のステップNext steps

CpuUseDemo アプリには目立った機能がないかもしれませんが、非同期操作やパフォーマンスと診断ハブの他のツールで試行錯誤することによって、ユーティリティを拡張することができます。The CpuUseDemo app is not the most brilliant of apps, but you can extend its utility by using it to experiment with asynchronous operation and other tools in the Performance and Diagnostics hub.

  • MainPage::<GetNumberAsync>b__b は、GetNumber メソッドの実行よりも、[外部コード] でより多くの時間を費やします。Note that MainPage::<GetNumberAsync>b__b spends more time in [External Code] than it does executing the GetNumber method. この時間の大半は、非同期操作のオーバーヘッドです。Much of this time is the overhead of the asynchronous operations. NUM_TASKS で、タスクの数を増やし (MainPage.xaml.cs の GetNumber 定数で設定)、イテレーションの数を減らしてみます (MIN_ITERATIONS の値を変更)。Try increasing the number of tasks (set in the NUM_TASKS constant of MainPage.xaml.cs) and reducing the number of iterations in GetNumber (change the MIN_ITERATIONS value). コレクション シナリオを実行し、MainPage::<GetNumberAsync>b__b の CPU アクティビティを元の CPU 使用率の診断セッションのものと比較します。Run the collection scenario and compare the CPU activity of MainPage::<GetNumberAsync>b__bto that in the original CPU Usage diagnostic session. タスクを減らし、イテレーションを増やしてみます。Try reducing the tasks and increasing the iterations.

  • ユーザーは多くの場合、アプリの実際のパフォーマンスは気にしていませんが、アプリの見かけ上のパフォーマンスや応答性は気にします。Users often don't care about the real performance of your app; they do care about the perceived performance and responsiveness of the app. XAML UI 応答性ツールは、見かけ上の応答性に影響するアクティビティの詳細を UI スレッドに表示します。The XAML UI Responsive tool shows you details of activity on the UI thread that effect perceived responsiveness.

    診断とパフォーマンス ハブで新しいセッションを作成し、XAML UI 応答性ツールと CPU 使用率ツールの両方を追加します。Create a new session in the Diagnostic and Performance hub, and add both the XAML UI Responsive tool and the CPU Usage tool. コレクション シナリオを実行します。Run the collection scenario. ここまで読み進めることができた場合、レポートには既にわかっていること以外の情報はないでしょう。しかし、2 つのメソッドの [UI スレッド使用状況] タイムライン グラフの違いは歴然としています。If you've read this far, the report probably doesn't tell you anything that you haven't already figured out, but the differences in the UI Thread utilization timeline graph for the two methods is striking. 複雑な実世界のアプリでは、ツールを組み合わせて使用することは非常に役立ちます。In complex, real-world apps, the combination of tools can be very helpful.

MainPage.xamlMainPage.xaml

<Page  
    x:Class="CpuUseDemo.MainPage"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    xmlns:local="using:CpuUseDemo"  
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
    mc:Ignorable="d">  

    <Page.Resources>  
        <Style TargetType="TextBox">  
            <Setter Property="FontFamily"  Value="Lucida Console" />  
        </Style>  
    </Page.Resources>  
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
        <Grid.RowDefinitions>  
            <RowDefinition Height="Auto" />  
            <RowDefinition Height="*" />  
        </Grid.RowDefinitions>  
        <StackPanel Grid.Row="0" Orientation="Horizontal"  Margin="0,40,0,0">  
            <Button Name="GetMaxNumberButton" Click="GetMaxNumberButton_Click"  Content="Get Max Number" />  
            <Button Name="GetMaxNumberAsyncButton" Click="GetMaxNumberAsyncButton_Click"  Content="Get Max Number Async" />  
        </StackPanel>  
        <StackPanel Grid.Row="1">  
            <TextBox Name="TextBox1" AcceptsReturn="True" />  
        </StackPanel>  
    </Grid>  

</Page>  

MainPage.xaml.csMainPage.xaml.cs

using System;  
using System.Collections.Generic;  
using System.IO;  
using System.Linq;  
using System.Runtime.InteropServices.WindowsRuntime;  
using Windows.Foundation;  
using Windows.Foundation.Collections;  
using Windows.UI.Xaml;  
using Windows.UI.Xaml.Controls;  
using Windows.UI.Xaml.Controls.Primitives;  
using Windows.UI.Xaml.Data;  
using Windows.UI.Xaml.Input;  
using Windows.UI.Xaml.Media;  
using Windows.UI.Xaml.Navigation;  
using Windows.Foundation.Diagnostics;  
using System.Threading;  
using System.Threading.Tasks;  
using System.Collections.Concurrent;  

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238  

namespace CpuUseDemo  
{  
    /// <summary>  
    /// An empty page that can be used on its own or navigated to within a Frame.  
    /// </summary>  
    public sealed partial class MainPage : Page  
    {  
        public MainPage()  
        {  
            this.InitializeComponent();  
        }  

        const int NUM_TASKS = 48;  
        const int MIN_ITERATIONS = int.MaxValue / 1000;  
        const int MAX_ITERATIONS = MIN_ITERATIONS + 10000;  

        long m_totalIterations = 0;  
        readonly object m_totalItersLock = new object();  

        private void GetMaxNumberButton_Click(object sender, RoutedEventArgs e)  
        {  
            GetMaxNumberAsyncButton.IsEnabled = false;  
            lock (m_totalItersLock)  
            {  
                m_totalIterations = 0;  
            }  
            List<int> tasks = new List<int>();  
            for (var i = 0; i < NUM_TASKS; i++)  
            {  
                var result = 0;  
                result = GetNumber();  
                tasks.Add(result);  
            }  
            var max = tasks.Max();  
            var s = GetOutputString("GetMaxNumberButton_Click", NUM_TASKS, max, m_totalIterations);  
            TextBox1.Text += s;  
            GetMaxNumberAsyncButton.IsEnabled = true;  
        }  

        private async void GetMaxNumberAsyncButton_Click(object sender, RoutedEventArgs e)  
        {  
            GetMaxNumberButton.IsEnabled = false;  
            GetMaxNumberAsyncButton.IsEnabled = false;  
            lock (m_totalItersLock)  
            {  
                m_totalIterations = 0;  
            }  
            var tasks = new ConcurrentBag<Task<int>>();  
            for (var i = 0; i < NUM_TASKS; i++)  
            {  
                tasks.Add(GetNumberAsync());  
            }  
            await Task.WhenAll(tasks.ToArray());  
            var max = 0;  
            foreach (var task in tasks)  
            {  
                max = Math.Max(max, task.Result);  
            }  
            var func = "GetMaxNumberAsyncButton_Click";  
            var outputText = GetOutputString(func, NUM_TASKS, max, m_totalIterations);  
            TextBox1.Text += outputText;  
            this.GetMaxNumberButton.IsEnabled = true;  
            GetMaxNumberAsyncButton.IsEnabled = true;  
        }  

        private int GetNumber()  
        {  
            var rand = new Random();  
            var iters = rand.Next(MIN_ITERATIONS, MAX_ITERATIONS);  
            var result = 0;  
            lock (m_totalItersLock)  
            {  
                m_totalIterations += iters;  
            }  
            // we're just spinning here  
            // and using Random to frustrate compiler optimizations  
            for (var i = 0; i < iters; i++)  
            {  
                result = rand.Next();  
            }  
            return result;  
        }  

        private Task<int> GetNumberAsync()  
        {  
            return Task<int>.Run(() =>  
            {  
                return GetNumber();  
            });  
        }  

        string GetOutputString(string func, int cycles, int max, long totalIters)  
        {  
            var fmt = "{0,-35}Tasks:{1,3}    Maximum:{2, 12}    Iterations:{3,12}\n";  
            return String.Format(fmt, func, cycles, max, totalIters);  
        }  

    }  
}  

関連項目See Also

Visual Studio のプロファイルProfiling in Visual Studio
プロファイリング機能ツアーProfiling Feature Tour