UWP(유니버설 Windows 앱)에서 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 사용량을 실행할 수 있습니다.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 유니버설 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 유니버설 앱을 만듭니다.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초간 기다린 다음 Get Max Number Async(비동기적으로 최대 수 가져오기)를 선택합니다.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. 두 번째 출력 줄이 나타나면 성능 및 진단 허브에서 수집 중지 를 선택합니다.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. 대용량 스파이크 두 개는 단추를 두 번 클릭할 경우의 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_Click 선택됨GetMaxNumberButton_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

1단계Step 1 CPU 사용량 호출 트리의 최상위 노드는 의사 노드입니다.The top-level node in CPU Usage call trees is a pseudo-node
2단계Step 2 대다수 앱에서 외부 코드 포시 옵션이 사용하지 않도록 설정되어 있는 경우 두 번째 수준 노드는 앱을 시작/중지하고, UI를 그리며, 스레드 일정을 제어하고, 앱에 다른 낮은 수준 서비스를 제공하는 시스템과 프레임워크 코드가 포함된 [External Code] 노드입니다.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.
3단계Step 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.
4단계Step 4 메서드의 자식 노드에는 부모 메서드 호출에 대한 데이터만 포함되어 있습니다.Child nodes of a method contain data only for the calls of the parent method. 외부 코드 표시 가 사용하지 않도록 설정되어 있으면 앱 메서드에 [External Code] 노드를 포함할 수 있습니다.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 사용량 호출 트리에서 사용자 메서드의 외부 함수를 하나의 [External Code] 노드로 수집합니다.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 함수가 포함된 모듈의 이름 또는 [External Code] 노드에 함수가 포함된 모듈의 수입니다.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. 비동기 메서드는 일반적으로 하나 이상의 서로 다른 스레드에서 실행되지만 항상 그렇지는 않습니다.The asynchronous methods are often, but not always, executed on one or more different threads. 이 코드는 트리의 상단 노드 바로 아래의 [External Code] 노드의 자식으로 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

[External Code] 아래에 있는 처음 두 노드는 상태 시스템 클래스의 컴파일러 생성 메서드입니다.The first two nodes under [External Code] are the compiler-generated methods of the state machine class. 세 번째 노드는 원래 메서드에 대한 호출입니다.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::MoveNextGetNumberAsync에 대한 호출을 래핑하는 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 메서드를 실행하는 것보다 [External Code]에서 더 많은 시간을 소모합니다.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. 작업 수를 늘리고(MainPage.xaml.cs의 NUM_TASKS 상수에서 설정) 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 사용량 진단 세션의 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. 여기까지 읽었다면 보고서 내용을 대부분 파악했겠지만, 두 메서드에 대한 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