Udostępnij za pośrednictwem


Samouczek: rozwiązywanie problemów z tworzeniem w czasie kompilacji funkcji

Użyj widoku Build Szczegółowe informacje Functions, aby rozwiązać problem z wpływem funkcji w czasie kompilacji w projektach języka C++.

Wymagania wstępne

  • Program Visual Studio 2022 w wersji 17.8 lub nowszej.
  • Szczegółowe informacje o kompilacji języka C++ są domyślnie włączone, jeśli zainstalujesz pakiet roboczy Programowanie aplikacji klasycznych za pomocą języka C++ lub programowanie gier z obciążeniem języka C++.

Zrzut ekranu przedstawiający Instalator programu Visual Studio z wybranym obciążeniem Programowanie aplikacji klasycznych w języku C++.

Zostanie wyświetlona lista zainstalowanych składników. Szczegółowe informacje kompilacji języka C++ jest wyróżniona i jest zaznaczona, co oznacza, że jest zainstalowany.

Zrzut ekranu przedstawiający Instalator programu Visual Studio z wybranym obciążeniem Programowanie gier w języku C++.

Zostanie wyświetlona lista zainstalowanych składników. Szczegółowe informacje kompilacji języka C++ jest wyróżniona i jest zaznaczona, co oznacza, że jest zainstalowany.

Omówienie

Tworzenie Szczegółowe informacje, teraz zintegrowane z programem Visual Studio, ułatwia optymalizowanie czasów kompilacji — szczególnie w przypadku dużych projektów, takich jak gry AAA. Kompilowanie Szczegółowe informacje zapewnia analizę, taką jak widok usługi Functions, która ułatwia diagnozowanie kosztownego generowania kodu w czasie kompilacji. Wyświetla on czas generowania kodu dla każdej funkcji i pokazuje wpływ funkcji __forceinline.

Dyrektywa __forceinline nakazuje kompilatorowi wbudowane działanie niezależnie od jego rozmiaru lub złożoności. Podkreślenie funkcji może zwiększyć wydajność środowiska uruchomieniowego, zmniejszając obciążenie wywołania funkcji. Kompromis polega na tym, że może zwiększyć rozmiar pliku binarnego i wpłynąć na czas kompilacji.

W przypadku zoptymalizowanych kompilacji czas spędzony na generowaniu kodu znacznie przyczynia się do łącznego czasu kompilacji. Ogólnie rzecz biorąc, optymalizacja funkcji języka C++ odbywa się szybko. W wyjątkowych przypadkach niektóre funkcje mogą stać się wystarczająco duże i wystarczająco złożone, aby wywierać presję na optymalizator i zauważalnie spowalniać kompilacje.

W tym artykule dowiesz się, jak używać widoku Build Szczegółowe informacje Functions w celu znalezienia wąskich gardeł w kompilacji.

Ustawianie opcji kompilacji

Aby zmierzyć wyniki __forceinlinepolecenia , użyj kompilacji wydania , ponieważ kompilacje debugowania nie są wbudowane __forceinline , ponieważ kompilacje debugowania używają przełącznika /Ob0 kompilatora, co powoduje wyłączenie tej optymalizacji. Ustaw kompilację dla wersji i x64:

  1. Na liście rozwijanej Konfiguracje rozwiązań wybierz pozycję Wydanie.
  2. Na liście rozwijanej Platformy rozwiązań wybierz pozycję x64.

Zrzut ekranu przedstawiający listę rozwijaną Konfiguracja rozwiązania ustawioną na Wydanie, a lista rozwijana Platforma rozwiązania ustawiona na x64.

Ustaw poziom optymalizacji na maksymalną optymalizację:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy nazwę projektu i wybierz polecenie Właściwości.

  2. We właściwościach projektu przejdź do pozycji C/C++>Optimization.

  3. Ustaw listę rozwijaną Optymalizacja na wartość Maksymalna optymalizacja (Przysługę) (/O2).

    Zrzut ekranu przedstawiający okno dialogowe stron właściwości projektu. Ustawienia są otwarte dla właściwości > konfiguracji C/C++ > Optimization. Lista rozwijana Optymalizacja jest ustawiona na wartość Maksymalna optymalizacja (faworyzowanie szybkości) (/O2).

  4. Kliknij przycisk OK , aby zamknąć okno dialogowe.

Uruchamianie Szczegółowe informacje kompilacji

W wybranym projekcie, a następnie przy użyciu opcji kompilacji wydania ustawionych w poprzedniej sekcji uruchom polecenie Kompiluj Szczegółowe informacje, wybierając z menu głównego polecenie Kompiluj>kompilację Uruchom kompilację Szczegółowe informacje przy odbudowywaniu zaznaczenia.> Możesz również kliknąć prawym przyciskiem myszy projekt w Eksploratorze rozwiązań i wybrać polecenie Uruchom kompilację Szczegółowe informacje> Rebuild. Wybierz pozycję Skompiluj zamiast kompilacji, aby zmierzyć czas kompilacji dla całego projektu, a nie tylko dla kilku plików, które mogą być teraz zanieczyszczone.

Zrzut ekranu przedstawiający menu główne z wybraną Szczegółowe informacje Uruchom kompilację w obszarze Ponowne kompilowanie zaznaczenia>.

Po zakończeniu kompilacji zostanie otwarty plik dziennika śledzenia zdarzeń (ETL). Jest on zapisywany w folderze wskazywany przez zmienną środowiskową systemu Windows TEMP . Wygenerowana nazwa jest oparta na czasie zbierania.

Widok funkcji

W oknie pliku ETL wybierz kartę Funkcje . Przedstawia funkcje, które zostały skompilowane, oraz czas generowania kodu dla każdej funkcji. Jeśli ilość kodu wygenerowanego dla funkcji jest niewielka, nie będzie ona wyświetlana na liście, aby uniknąć obniżenia wydajności zbierania zdarzeń kompilacji.

Zrzut ekranu przedstawiający plik widoku build Szczegółowe informacje Functions.

W kolumnie Nazwa funkcji funkcja performPhysicsCalculations() jest wyróżniona i oznaczona ikoną ognia.:::

Kolumna Time [sec, %] pokazuje, jak długo zajęło skompilowanie każdej funkcji w czasie odpowiedzialności zegara ściany (WCTR). Ta metryka rozkłada czas zegara ściany między funkcje na podstawie ich użycia równoległych wątków kompilatora. Jeśli na przykład dwa różne wątki kompilują dwie różne funkcje jednocześnie w ciągu jednego sekundy, funkcja WCTR każdej funkcji jest rejestrowana jako 0,5 sekundy. Odzwierciedla to proporcjonalny udział każdej funkcji w łącznym czasie kompilacji, biorąc pod uwagę zasoby, które są używane podczas wykonywania równoległego. W związku z tym usługa WCTR zapewnia lepszą miarę wpływu każdej funkcji na ogólny czas kompilacji w środowiskach, w których występuje wiele działań kompilacji jednocześnie.

Kolumna Forceinline Size (Rozmiar elementu Forceinline) pokazuje mniej więcej liczbę instrukcji wygenerowanych dla funkcji. Kliknij cudzysłów przed nazwą funkcji, aby zobaczyć poszczególne wbudowane funkcje, które zostały rozwinięte w tej funkcji, w przybliżeniu ile instrukcji zostało wygenerowanych dla każdego.

Listę można posortować, klikając kolumnę Czas , aby zobaczyć, które funkcje zajmują najwięcej czasu na skompilowanie. Ikona "fire" wskazuje, że koszt generowania tej funkcji jest wysoki i warto zbadać. Nadmierne użycie __forceinline funkcji może znacznie spowolnić kompilację.

Określoną funkcję można wyszukać przy użyciu pola Funkcje filtru. Jeśli czas generowania kodu funkcji jest za mały, nie jest wyświetlany w widoku funkcji .

Poprawianie czasu kompilacji przez dostosowanie inliningu funkcji

W tym przykładzie performPhysicsCalculations funkcja zajmuje jak najwięcej czasu na skompilowanie.

Zrzut ekranu przedstawiający widok build Szczegółowe informacje Functions.

W kolumnie Nazwa funkcji funkcja performPhysicsCalculations() jest wyróżniona i oznaczona ikoną ognia.

Dalsze badanie przez wybranie cudzysłów przed tym funkcją, a następnie posortowanie kolumny Forceinline Size z najwyższego do najniższego, widzimy największych współautorów problemu.

Zrzut ekranu przedstawiający widok Build Szczegółowe informacje Functions z rozszerzoną funkcją.

funkcja performPhysicsCalculations() jest rozwinięta i przedstawia długą listę funkcji, które zostały w nim wbudowane. Istnieje wiele wystąpień funkcji, takich jak complexOperation(), recursiveHelper() i sin(). Kolumna Forceinline Size pokazuje, że complexOperation() jest największą funkcją w tekście na 315 instrukcji. RecursiveHelper() zawiera 119 instrukcji. Sin() ma 75 instrukcji, ale istnieje o wiele więcej wystąpień niż inne funkcje.

Istnieje kilka większych wbudowanych funkcji, takich jak Vector2D<float>::complexOperation() i Vector2D<float>::recursiveHelper() , które przyczyniają się do problemu. Istnieje jednak wiele innych wystąpień (nie wszystkie pokazane tutaj) elementów Vector2d<float>::sin(float), Vector2d<float>::cos(float), Vector2D<float>::power(float,int)i Vector2D<float>::factorial(int). Po dodaniu tych instrukcji łączna liczba wygenerowanych instrukcji szybko przekracza kilka większych wygenerowanych funkcji.

Patrząc na te funkcje w kodzie źródłowym, widzimy, że czas wykonywania zostanie spędzone wewnątrz pętli. Na przykład oto kod dla factorial()elementu :

static __forceinline T factorial(int n)
{
    T result = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < i; ++j) {
            result *= (i - j) / (T)(j + 1);
        }
    }
    return result;
}

Być może ogólny koszt wywoływania tej funkcji jest nieznaczny w porównaniu z kosztem samej funkcji. Tworzenie wbudowanej funkcji jest najbardziej korzystne, gdy czas potrzebny na wywołanie funkcji (wypychanie argumentów na stosie, przechodzenie do funkcji, wyświetlanie argumentów zwracanych i zwracanie z funkcji) jest mniej więcej podobne do czasu potrzebnych do wykonania funkcji, a gdy funkcja jest wywoływana dużo. Jeśli tak nie jest, może istnieć malejące zwroty z tworzenia go w tekście. Możemy spróbować usunąć z niej dyrektywę __forceinline , aby sprawdzić, czy pomaga czas kompilacji. Kod dla powersin() elementu i cos() jest podobny do tego, że kod składa się z pętli, która będzie wykonywana wiele razy. Możemy również spróbować usunąć dyrektywę __forceinline z tych funkcji.

Uruchomimy ponownie Szczegółowe informacje kompilacji z menu głównego, wybierając pozycję Kompiluj kompilację Uruchom kompilację Szczegółowe informacje po ponownym skompilowaniu zaznaczenia>.> Możesz również kliknąć prawym przyciskiem myszy projekt w Eksploratorze rozwiązań i wybrać polecenie Uruchom kompilację Szczegółowe informacje> Rebuild. Wybieramy opcję Kompiluj zamiast Kompilacja , aby zmierzyć czas kompilacji dla całego projektu, tak jak poprzednio, a nie tylko dla kilku plików, które mogą być teraz zanieczyszczone.

Czas kompilacji trwa od 25,181 sekund do 13,376 sekund, a performPhysicsCalculations funkcja nie jest już wyświetlana w widoku funkcji , ponieważ nie przyczynia się wystarczająco dużo do czasu kompilacji do zliczenia.

Zrzut ekranu przedstawiający plik nagłówka wektora 2D.

W kolumnie Nazwa funkcji funkcja performPhysicsCalculations() jest wyróżniona i oznaczona ikoną ognia.:::

Czas sesji diagnostyki to całkowity czas wykonywania kompilacji oraz wszelkie nakłady pracy związane z gromadzeniem danych kompilacji Szczegółowe informacje.

Następnym krokiem będzie profilowanie aplikacji w celu sprawdzenia, czy wydajność aplikacji ma negatywny wpływ na zmianę. Jeśli tak jest, możemy selektywnie dodać __forceinline z powrotem zgodnie z potrzebami.

Kliknij dwukrotnie, kliknij prawym przyciskiem myszy lub naciśnij klawisz Enter w pliku w widoku Funkcje , aby otworzyć kod źródłowy dla tego pliku.

Zrzut ekranu przedstawiający kliknięcie prawym przyciskiem myszy pliku w widoku funkcje. Opcja menu Przejdź do pliku źródłowego jest wyróżniona.

Wskazówki

  • Możesz zapisać plik>jako plik ETL w bardziej trwałej lokalizacji, aby zachować rekord czasu kompilacji. Następnie możesz porównać ją z przyszłymi kompilacjami, aby sprawdzić, czy zmiany skracają czas kompilacji.
  • Jeśli przypadkowo zamkniesz okno Kompilacja Szczegółowe informacje, otwórz go ponownie, wyszukując <dateandtime>.etl plik w folderze tymczasowym. Zmienna TEMP środowiskowa systemu Windows zawiera ścieżkę folderu plików tymczasowych.
  • Aby zagłębić się w dane kompilacji Szczegółowe informacje przy użyciu usługi Windows Analizator wydajności (WPA), kliknij przycisk Otwórz w programie WPA w prawym dolnym rogu okna ETL.
  • Przeciągnij kolumny, aby zmienić kolejność kolumn. Na przykład możesz wolisz przenieść kolumnę Time ( Czas ) jako pierwszą kolumnę. Możesz ukryć kolumny, klikając prawym przyciskiem myszy nagłówek kolumny i usuwając zaznaczenie kolumn, których nie chcesz zobaczyć.
  • Widok Funkcje zawiera pole filtru umożliwiające znalezienie interesującej Cię funkcji. Wykonuje częściowe dopasowania w podanej nazwie.
  • Jeśli zapomnisz, jak interpretować, co widok funkcji próbuje wyświetlić, umieść kursor na karcie, aby wyświetlić etykietkę narzędzia opisjącą widok. Jeśli zatrzymasz wskaźnik myszy na karcie Funkcje , etykietka narzędzia informuje: "Wyświetl, który pokazuje statystyki funkcji, w których węzły podrzędne są funkcjami wymuszonych".

Rozwiązywanie problemów

  • Jeśli nie zostanie wyświetlone okno Kompilowanie Szczegółowe informacje, wykonaj ponowną kompilację zamiast kompilacji. Okno Kompilowanie Szczegółowe informacje nie jest wyświetlane, jeśli faktycznie nic nie zostanie skompiluje, co może mieć miejsce, jeśli żadne pliki nie uległy zmianie od ostatniej kompilacji.
  • Jeśli widok usługi Functions nie wyświetla żadnych funkcji, być może nie będziesz kompilować przy użyciu odpowiednich ustawień optymalizacji. Upewnij się, że kompilujesz wydanie z pełnymi optymalizacjami, zgodnie z opisem w temacie Ustawianie opcji kompilacji. Ponadto jeśli czas generowania kodu funkcji jest za mały, nie jest wyświetlany na liście.

Zobacz też

Funkcje wbudowane (C++)
Szybsze kompilacje języka C++, uproszczone: nowa metryka na czas
Kompilowanie Szczegółowe informacje w programie Visual Studio — Pure Virtual C++ 2023
Rozwiązywanie problemów z wpływem pliku nagłówka na czas kompilacji
Widok funkcji dla Szczegółowe informacje kompilacji w programie Visual Studio 2022 17.8
Samouczek: vcperf i Windows Analizator wydajności
Skracanie czasu generowania kodu za pomocą Szczegółowe informacje kompilacji języka C++