Share via


코드를 최적화하고 컴퓨팅 비용을 줄이기 위한 초보자 가이드(C#, Visual Basic, C++, F#)

컴퓨팅 시간을 줄이면 비용이 절감되므로 코드를 최적화하면 비용을 절감할 수 있습니다. 이 문서에서는 다양한 프로파일링 도구를 사용하여 이 작업을 수행하는 데 도움이 되는 방법을 보여 줍니다.

여기서는 단계별 지침을 제공하는 대신 프로파일링 도구를 효과적으로 사용하는 방법과 데이터를 해석하는 방법을 보여 줍니다. CPU 사용량 도구는 애플리케이션에서 컴퓨팅 리소스가 사용되는 위치를 캡처하고 시각화하는 데 도움이 될 수 있습니다. 호출 트리 및 플레임 그래프와 같은 CPU 사용량 보기는 앱에서 시간이 소요되는 위치에 대한 멋진 그래픽 시각화를 제공합니다. 또한 자동 인사이트는 큰 영향을 미칠 수 있는 정확한 최적화를 표시할 수 있습니다. 다른 프로파일링 도구도 문제를 격리하는 데 도움이 될 수 있습니다. 도구를 비교하려면 어떤 도구를 선택해야 하나요?를 참조하세요.

조사 시작

  • CPU 사용량 추적을 사용하여 조사를 시작합니다. CPU 사용량 도구는 성능 조사를 시작하고 비용을 줄이기 위해 코드를 최적화하는 데 도움이 되는 경우가 많습니다.
  • 다음으로, 문제를 격리하거나 성능을 향상시키는 데 도움이 되는 추가 인사이트를 원하는 경우 다른 프로파일링 도구 중 하나를 사용하여 추적을 수집하는 것을 고려합니다. 예시:
    • 메모리 사용량을 살펴보세요. .NET의 경우 먼저 .NET 개체 할당 도구를 사용해 보세요. .NET 또는 C++의 경우 메모리 사용량 도구를 확인할 수 있습니다.
    • 앱에서 파일 I/O를 사용하는 경우 파일 I/O 도구를 사용합니다.
    • ADO.NET 또는 Entity Framework를 사용하는 경우 데이터베이스 도구를 사용하여 SQL 쿼리, 정확한 쿼리 시간 등을 검사할 수 있습니다.

데이터 컬렉션 예시

이 문서에 표시된 예시 스크린샷은 블로그 및 관련 블로그 게시물의 데이터베이스에 대해 쿼리를 실행하는 .NET 앱을 기반으로 합니다. 먼저 CPU 사용량 추적을 검사하여 코드를 최적화하고 컴퓨팅 비용을 줄일 기회를 찾습니다. 무슨 일이 일어나고 있는지에 대한 일반적인 아이디어를 얻은 후에는 문제를 격리하는 데 도움이 되는 다른 프로파일링 도구의 추적도 살펴봅니다.

데이터 컬렉션에는 다음 단계가 필요합니다(여기에 표시되지 않음).

  • 릴리스 빌드로 앱 설정
  • 성능 프로파일러(Alt+F2)에서 CPU 사용량 도구를 선택합니다. (이후 단계에는 몇 가지 다른 도구가 포함됩니다.)
  • 성능 프로파일러에서 앱을 시작하고 추적을 수집합니다.

높은 CPU 사용량 영역 검사

먼저 CPU 사용량 도구를 사용하여 추적을 수집합니다. 진단 데이터가 로드되면 먼저 Top Insights 및 핫 경로를 보여 주는 초기 .diagsession 보고서 페이지를 검사. 핫 경로는 앱에서 CPU 사용량이 가장 높은 코드 경로를 보여 줍니다. 이러한 섹션에서는 개선할 수 있는 성능 문제를 빠르게 식별하는 데 도움이 되는 팁을 제공할 수 있습니다.

호출 트리 보기에서 핫 경로를 볼 수도 있습니다. 이 보기를 열려면 보고서에서 세부 정보 열기 링크를 사용한 다음 통화 트리를 선택합니다.

이 보기에서는 앱의 CPU 사용량의 약 60%를 사용하여 앱의 메서드에 대한 GetBlogTitleX 높은 CPU 사용량을 보여 줍니다. 그러나 자체 CPUGetBlogTitleX 은 낮으며 약 .10%에 불과합니다. 총 CPU와 달리 자체 CPU 값은 다른 함수에서 소요된 시간을 제외하므로 실제 병목 현상에 대한 호출 트리 뷰를 더 자세히 살펴보는 것을 알고 있습니다.

CPU 사용량 도구의 호출 트리 뷰 스크린샷.

GetBlogTitleX 는 매우 높은 자체 CPU 값에 의해 입증된 대로 대부분의 CPU 시간을 사용하는 두 개의 LINQ DLL에 대한 외부 호출을 만듭니다. LINQ 쿼리를 최적화할 영역으로 찾으려는 첫 번째 단서입니다.

자체 CPU가 하이라이트 표시된 CPU 사용량 도구의 호출 트리 뷰 스크린샷.

시각화된 호출 트리와 데이터의 다른 보기를 얻으려면 불꽃 그래프 보기로 전환합니다(호출 트리동일한 목록에서 선택). 여기서도 메서드가 앱의 많은 CPU 사용량(노란색으로 표시됨)을 담당하는 것처럼 GetBlogTitleX 보입니다. LINQ DLL에 대한 외부 호출은 상자 아래에 GetBlogTitleX 표시되며 메서드에 대한 모든 CPU 시간을 사용합니다.

CPU 사용량 도구의 플레임 그래프 뷰 스크린샷.

추가 데이터 수집

종종 다른 도구는 분석을 돕고 문제를 격리하는 데 도움이 되는 추가 정보를 제공할 수 있습니다. 이 예에서는 다음과 같은 접근 방식을 사용합니다.

  • 먼저 메모리 사용량을 살펴보겠습니다. 높은 CPU 사용량과 높은 메모리 사용량 간에 상관 관계가 있을 수 있으므로 문제를 격리하기 위해 둘 다 살펴보는 것이 유용할 수 있습니다.
  • LINQ DLL에 대해 알아봤으므로, 데이터베이스 도구도 살펴보겠습니다.

메모리 사용량 확인

메모리 사용량 측면에서 앱에서 무슨 일이 일어나고 있는지 확인하려면 .NET 개체 할당 도구를 사용하여 추적을 수집합니다(C++의 경우 메모리 사용량 도구를 대신 사용). 메모리 추적의 호출 트리 보기는 활성 경로를 표시하고 메모리 사용량이 많은 영역을 식별하는 데 도움이 됩니다. 이 시점에서는 메서드가 GetBlogTitleX 많은 개체를 생성하는 것처럼 보입니다. 실제로 900,000개 이상의 개체 할당이 있습니다.

.NET 개체 할당 도구의 호출 트리 뷰 스크린샷.

대부분의 개체는 문자열, 개체 배열 및 Int32입니다. 소스 코드를 검사하여 이러한 형식이 어떻게 생성되는지 확인할 수 있습니다.

데이터베이스 도구에서 쿼리 확인

CPU 사용량과 함께 데이터베이스 도구를 여러 개 선택할 수 있습니다. 추적을 수집한 경우 진단 페이지에서 쿼리 탭을 선택합니다. 데이터베이스 추적에 대한 쿼리 탭에서 첫 번째 행에 가장 긴 쿼리 2446ms가 표시되는 것을 볼 수 있습니다. 레코드 열은 쿼리가 읽는 레코드 수를 보여줍니다. 나중에 비교할 때 이 정보를 사용할 수 있습니다.

데이터베이스 도구의 데이터베이스 쿼리 스크린샷.

쿼리 열에서 LINQ에서 생성된 SELECT 문을 검사하여 첫 번째 행을 GetBlogTitleX 메서드와 연결된 쿼리로 식별합니다. 전체 쿼리 문자열을 보려면 필요한 경우 열 너비를 확장합니다. 전체 쿼리 문자열입니다:

SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"

여기서는 필요한 것보다 많은 열 값을 검색하고 있습니다. 소스 코드를 살펴보겠습니다.

최적화 코드

이제 소스 코드를 살펴볼 차례입니다 GetBlogTitleX . 데이터베이스 도구에서 쿼리를 마우스 오른쪽 버튼으로 클릭하고 원본 파일로 이동을 선택합니다. 원본 코드 GetBlogTitleX에서 LINQ를 사용하여 데이터베이스를 읽는 다음 코드를 찾습니다.

foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
  {
    foreach (var post in blog.Posts)
    {
      if (post.Author == "Fred Smith")
      {
        Console.WriteLine($"Post: {post.Title}");
      }
  }
}

이 코드는 루프를 사용하여 foreach "Fred Smith"를 작성자로 사용하여 데이터베이스에서 블로그를 검색합니다. 이를 살펴보면 데이터베이스의 각 블로그에 대한 새 개체 배열, 각 URL에 대한 연결된 문자열, 블로그 ID와 같은 게시물에 포함된 속성 값 등 많은 개체가 메모리에 생성되는 것을 볼 수 있습니다.

약간의 연구를 수행하고 LINQ 쿼리를 최적화하고 이 코드를 마련하는 방법에 대한 몇 가지 일반적인 권장 사항을 찾을 수 있습니다.

foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
  Console.WriteLine("Post: " + x);
}

이 코드에서는 쿼리를 최적화하는 데 도움이 되도록 몇 가지 변경 작업을 수행했습니다.

  • Where 절을 추가하고 foreach 루프 중 하나를 제거합니다.
  • 이 예시에서는 Select 문에 제목 속성만 프로젝션했습니다.

다음으로 프로파일링 도구를 사용하여 다시 테스트합니다.

결과 확인

코드를 업데이트한 후 CPU 사용량 도구를 다시 실행하여 추적을 수집합니다. 호출 트리 보기는 앱 CPU 총의 37%를 사용하여 1754ms만 실행되고 있음을 보여 GetBlogTitleX 하며, 이는 59%에서 크게 향상되었습니다.

CPU 사용량 도구의 호출 트리 뷰에서 개선된 CPU 사용량의 스크린샷.

다른 개선 시각화를 보려면 화염 그래프 보기로 전환합니다. 이 보기 GetBlogTitleX 에서는 CPU의 더 작은 부분도 사용합니다.

CPU 사용량 도구의 플레임 그래프 뷰에서 개선된 CPU 사용량의 스크린샷.

데이터베이스 도구 추적의 결과를 확인하고 100,000개 대신 이 쿼리를 사용하여 두 개의 레코드만 읽습니다. 또한 쿼리는 훨씬 간소화되어 이전에 생성된 불필요한 LEFT JOIN을 제거합니다.

데이터베이스 도구의 더 빠른 쿼리 시간 스크린샷.

다음으로, .NET 개체 할당 도구의 결과를 다시 검사 56,000개의 개체 할당만 담당하며 GetBlogTitleX 900,000개에서 거의 95% 감소합니다.

.NET 개체 할당 도구의 감소된 메모리 할당 스크린샷.

Iterate

여러 최적화가 필요할 수 있으며 코드 변경 내용을 계속 반복하여 성능을 향상시키고 컴퓨팅 비용을 줄일 수 있는 변경 내용을 확인할 수 있습니다.

다음 단계

다음 문서 및 블로그 게시물은 Visual Studio 성능 도구를 효과적으로 사용하는 방법을 배우는 데 도움이 되는 자세한 정보를 제공합니다.