컴퓨팅 시간을 줄이면 비용이 절감되므로 코드를 최적화하면 비용을 절감할 수 있습니다. 이 사례 연구에서는 성능 문제가 있는 샘플 애플리케이션을 사용하여 프로파일링 도구를 사용하여 효율성을 개선하는 방법을 보여 줍니다. 프로파일링 도구를 비교하려면 어떤 도구를 선택해야 하나요?
이 사례 연구에서는 다음 항목을 다룹니다.
- 코드 최적화의 중요성과 컴퓨팅 비용 절감에 미치는 영향.
- Visual Studio 프로파일링 도구를 사용하여 애플리케이션 성능을 분석하는 방법입니다.
- 이러한 도구에서 제공하는 데이터를 해석하여 성능 병목 상태를 식별하는 방법입니다.
- CPU 사용량, 메모리 할당 및 데이터베이스 상호 작용에 중점을 두고 코드를 최적화하기 위해 실용적인 전략을 적용하는 방법입니다.
따라가서 이러한 기술을 사용자 고유의 애플리케이션에 적용하여 보다 효율적이고 비용 효율적으로 만듭니다.
최적화 사례 연구
이 사례 연구에서 검토된 샘플 애플리케이션은 블로그 및 블로그 게시물의 데이터베이스에 대해 쿼리를 실행하는 .NET 애플리케이션입니다. .NET용 인기 ORM(Object-Relational 매핑)인 Entity Framework를 활용하여 SQLite 로컬 데이터베이스와 상호 작용합니다. 애플리케이션은 많은 수의 쿼리를 실행하도록 구조화되어 있으며, 광범위한 데이터 검색 작업을 처리하는 데 .NET 애플리케이션이 필요할 수 있는 실제 시나리오를 시뮬레이션합니다. 샘플 애플리케이션은 시작 샘플 Entity Framework의 수정된 버전입니다.
샘플 애플리케이션의 주요 성능 문제는 컴퓨팅 리소스를 관리하고 데이터베이스와 상호 작용하는 방법에 있습니다. 애플리케이션의 효율성에 큰 영향을 주는 성능 병목 현상이 있으며, 따라서 애플리케이션 실행과 관련된 컴퓨팅 비용이 발생합니다. 이 문제에는 다음과 같은 증상이 포함됩니다.
높은 CPU 사용량 : 애플리케이션은 불필요하게 많은 양의 CPU 리소스를 소비하는 방식으로 비효율적인 계산 또는 처리 작업을 수행할 수 있습니다. 이로 인해 응답 시간이 느려지고 운영 비용이 증가할 수 있습니다.
비효율적인 메모리 할당: 애플리케이션은 경우에 따라 메모리 사용 및 할당과 관련된 문제에 직면할 수 있습니다. .NET 앱에서 비효율적인 메모리 관리로 인해 가비지 수집이 증가하여 애플리케이션 성능에 영향을 줄 수 있습니다.
데이터베이스 상호 작용 오버헤드: 데이터베이스에 대해 많은 수의 쿼리를 실행하는 애플리케이션은 데이터베이스 상호 작용과 관련된 병목 상태를 경험할 수 있습니다. 여기에는 비효율적인 쿼리, 과도한 데이터베이스 호출 및 Entity Framework 기능의 잘못된 사용이 포함되며, 모두 성능이 저하할 수 있습니다.
사례 연구는 Visual Studio의 프로파일링 도구를 사용하여 애플리케이션의 성능을 분석하여 이러한 문제를 해결하는 것을 목표로 합니다. 개발자는 애플리케이션의 성능을 개선할 수 있는 위치와 방법을 이해하여 최적화를 구현하여 CPU 사용량을 줄이고 메모리 할당 효율성을 개선하며 데이터베이스 상호 작용을 간소화하고 리소스 사용률을 최적화할 수 있습니다. 궁극적인 목표는 애플리케이션의 전반적인 성능을 향상시켜 더 효율적이고 비용 효율적인 실행을 만드는 것입니다.
도전
샘플 .NET 애플리케이션에서 성능 문제를 해결하면 몇 가지 문제가 발생합니다. 이러한 문제는 성능 병목 상태를 진단하는 복잡성에서 비롯됩니다. 설명된 문제 해결의 주요 과제는 다음과 같습니다.
성능 병목 상태 진단: 주요 과제 중 하나는 성능 문제의 근본 원인을 정확하게 식별하는 것입니다. 높은 CPU 사용량, 비효율적인 메모리 할당 및 데이터베이스 상호 작용 오버헤드에는 여러 가지 원인이 있을 수 있습니다. 개발자는 프로파일링 도구를 효과적으로 사용하여 이러한 문제를 진단해야 하며, 이러한 도구의 작동 방식과 출력을 해석하는 방법을 이해해야 합니다.
지식 및 리소스 제약 조건: 마지막으로 팀은 지식, 전문 지식 및 리소스와 관련된 제약 조건에 직면할 수 있습니다. 애플리케이션을 프로파일링하고 최적화하려면 특정 기술과 경험이 필요하며 모든 팀이 이러한 리소스에 즉시 액세스할 수 있는 것은 아닙니다.
이러한 문제를 해결하려면 프로파일링 도구, 기술 지식, 신중한 계획 및 테스트의 효과적인 사용을 결합하는 전략적 접근 방식이 필요합니다. 사례 연구는 이러한 문제를 극복하고 애플리케이션의 성능을 개선하기 위한 전략과 통찰력을 제공하여 이 프로세스를 통해 개발자를 안내하는 것을 목표로 합니다.
전략
다음은 이 사례 연구에서의 접근 방식에 대한 개략적인 보기입니다.
- CPU 사용량 추적을 사용하여 조사를 시작합니다. Visual Studio의 CPU 사용량 도구 성능 조사를 시작하고 비용을 줄이기 위해 코드를 최적화하는 데 도움이 되는 경우가 많습니다.
- 다음으로, 문제를 격리하거나 성능을 향상시키는 데 도움이 되는 추가 인사이트를 얻기 위해 다른 프로파일링 도구 중 하나를 사용하여 추적을 수집합니다. 예를 들어:
- 메모리 사용량을 살펴보겠습니다. .NET의 경우 먼저 .NET 개체 할당 도구 시도합니다. (.NET 또는 C++의 경우 메모리 사용량 도구를 대신 살펴볼 수 있습니다.)
- ADO.NET 또는 Entity Framework의 경우 데이터베이스 도구 사용하여 SQL 쿼리, 정확한 쿼리 시간 등을 검사할 수 있습니다.
데이터 수집에는 다음 작업이 필요합니다.
- 앱을 릴리스 빌드로 설정합니다.
- 성능 프로파일러(Alt+F2)에서 CPU 사용량 도구를 선택합니다. (이후 단계에는 몇 가지 다른 도구가 포함됩니다.)
- 성능 프로파일러에서 앱을 시작하고 추적을 수집합니다.
높은 CPU 사용량 영역 검사
CPU 사용량 도구를 사용하여 추적을 수집하고 Visual Studio에 로드한 후 먼저 요약된 데이터를 보여 주는 초기 .diagsession 보고서 페이지를 확인합니다. 보고서에서 세부 정보 열기 링크를 사용하세요.
보고서 세부 정보 보기에서 호출 트리 보기를 엽니다. 앱에서 CPU 사용량이 가장 높은 코드 경로를 핫 경로라고 합니다. 핫 경로 불꽃 아이콘()은 개선될 수 있는 성능 문제를 빠르게 식별하는 데 도움이 될 수 있습니다.
호출 트리 보기에서는 앱의 CPU 사용량에서 약 60개의% 공유를 사용하여 앱의 GetBlogTitleX
메서드에 대한 높은 CPU 사용량을 볼 수 있습니다. 그러나 GetBlogTitleX
의 자체 CPU 값이 약 .10%로 낮습니다.
전체 CPU와 달리, 자체 CPU 값은 다른 함수에서 소요된 시간을 제외합니다. 따라서 우리는 실제 병목 현상을 찾기 위해 호출 트리에서 더 아래를 살펴봐야 합니다.
GetBlogTitleX
가 CPU 시간을 가장 많이 사용하는 두 개의 LINQ DLL 파일을 외부에서 호출한다는 사실은 매우 높은 자체 CPU 값으로 입증됩니다. LINQ 쿼리가 최적화할 영역일 수 있다는 첫 번째 단서입니다.
시각화된 호출 트리와 데이터의 다른 보기를 얻으려면 플레임 그래프 보기를 엽니다. (또는 GetBlogTitleX
마우스 오른쪽 단추로 클릭하고 불꽃 그래프에서 보기를 선택합니다.) 여기서도 GetBlogTitleX
메서드가 많은 앱의 CPU 사용량(노란색으로 표시됨)을 담당하는 것처럼 보입니다. LINQ DLL에 대한 외부 호출은 GetBlogTitleX
상자 아래에 표시되며 메서드에 대한 모든 CPU 시간을 사용합니다.
추가 데이터 수집
종종 다른 도구는 분석을 돕고 문제를 격리하는 데 도움이 되는 추가 정보를 제공할 수 있습니다. 이 사례 연구에서는 다음과 같은 방법을 사용합니다.
- 먼저 메모리 사용량을 살펴보세요. 높은 CPU 사용량과 높은 메모리 사용량 간에 상관 관계가 있을 수 있으므로 문제를 격리하기 위해 둘 다 살펴보는 것이 유용할 수 있습니다.
- LINQ DLL을 식별했으므로 데이터베이스 도구도 살펴보겠습니다.
메모리 사용량 확인
메모리 사용량 측면에서 앱에서 어떤 일이 일어나고 있는지 확인하기 위해 .NET 개체 할당 도구를 사용하여 추적을 수집합니다(C++의 경우 메모리 사용량 도구를 대신 사용할 수 있습니다). 메모리 추적의 호출 트리 보기는 활성 경로를 표시하며 메모리 사용량이 많은 영역을 식별하는 데 도움이 됩니다. 이 시점에서 GetBlogTitleX
메서드는 많은 개체를 생성하는 것처럼 보입니다. 실제로 900,000개 이상의 개체 할당이 있습니다.
대부분의 개체는 문자열, 개체 배열 및 Int32s입니다. 소스 코드를 검사하여 이러한 형식이 어떻게 생성되는지 확인할 수 있습니다.
데이터베이스 도구에서 쿼리 확인
성능 프로파일러에서 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 쿼리를 최적화하는 방법에 대한 몇 가지 일반적인 권장 사항을 찾습니다. 다른 방법으로는 시간을 절약하고 코파일럿이 우리를 위해 조사를 하도록 할 수 있습니다.
Copilot를 사용하는 경우, 콘텍스트 메뉴에서 Ask Copilot을 선택하고 다음 질문을 입력합니다.
Can you make the LINQ query in this method faster?
팁
/optimize 같은 슬래시 명령을 사용하여 Copilot에 대한 좋은 질문을 작성할 수 있습니다.
이 예제에서 Copilot는 설명과 함께 다음과 같은 제안된 코드 변경 내용을 제공합니다.
public void GetBlogTitleX()
{
var posts = db.Posts
.Where(post => post.Author == "Fred Smith")
.Select(post => post.Title)
.ToList();
foreach (var postTitle in posts)
{
Console.WriteLine($"Post: {postTitle}");
}
}
이 코드에는 쿼리를 최적화하는 데 도움이 되는 몇 가지 변경 내용이 포함되어 있습니다.
-
Where
절을 추가하고foreach
루프 중 하나를 제거했습니다. - 이 예제에서 필요한
Select
문에 Title 속성만 투영했습니다.
다음으로 프로파일링 도구를 사용하여 다시 테스트합니다.
결과
코드를 업데이트한 후 CPU 사용량 도구를 다시 실행하여 추적을 수집합니다.
호출 트리 보기는 GetBlogTitleX
가 앱의 CPU 총량 중 37%를 사용하여 1,754ms만 실행되고 있음을 보여주며, 이는 이전의 59%에서 상당히 개선된 것입니다.
플레임 그래프 보기로 전환하여 개선 사항을 보여주는 다른 시각화를 확인합니다. 이 보기에서 GetBlogTitleX
CPU의 더 작은 부분도 사용합니다.
데이터베이스 도구 추적의 결과를 확인하고 100,000개 대신 이 쿼리를 사용하여 두 개의 레코드만 읽습니다. 또한 쿼리는 훨씬 간소화되어 이전에 생성된 불필요한 LEFT JOIN을 제거합니다.
다음으로. .NET 개체 할당 도구에서 결과를 다시 확인하고 GetBlogTitleX
56,000개의 개체 할당만 담당하며 900,000개에서 거의 95% 감소하는 것을 확인합니다.
반복하다
여러 최적화가 필요할 수 있으며 코드 변경을 계속 반복하여 성능을 향상시키고 컴퓨팅 비용을 줄이는 데 도움이 되는 변경 내용을 확인할 수 있습니다.
다음 단계
다음 문서 및 블로그 게시물은 Visual Studio 성능 도구를 효과적으로 사용하는 방법을 배우는 데 도움이 되는 자세한 정보를 제공합니다.
- 사례 연구: 성능 문제를 격리하기
- 사례 연구: 30분 미만의 두 배 성능
- 새 계측 도구 사용하여 Visual Studio 성능 향상