Wydajność debugowania i wydania

133

Napotkałem następujący akapit:

„Ustawienie Debug vs. Release w IDE podczas kompilowania kodu w programie Visual Studio nie ma prawie żadnego wpływu na wydajność… wygenerowany kod jest prawie taki sam. Kompilator C # tak naprawdę nie wykonuje żadnej optymalizacji. Kompilator C # po prostu wypluwa IL… a w czasie wykonywania to JITer wykonuje całą optymalizację. JITer ma tryb debugowania / wydania, co ma ogromny wpływ na wydajność. Ale to nie ma znaczenia, niezależnie od tego, czy uruchamiasz konfigurację debugowania, czy wydania projektu, która powoduje wyłączenie debugera ”.

Źródło jest tutaj, a podcast jest tutaj .

Czy ktoś może skierować mnie do artykułu firmy Microsoft, który faktycznie może to udowodnić?

Wyszukiwanie w Google „ Debugowanie w języku C # a wydajność wydania ” w większości zwraca wyniki, takie jak „ Debugowanie ma duży wpływ na wydajność ”, „ Wydanie jest zoptymalizowane ” i „ Nie wdrażaj debugowania w środowisku produkcyjnym ”.

sagie
źródło
W przypadku .Net4 na Win7-x86 mam program o ograniczonej mocy obliczeniowej, który napisałem, który działa prawie 2x szybciej w wydaniu niż debugowanie bez asserts / etc w głównej pętli.
Bengie
Ponadto, jeśli zależy Ci na wykorzystaniu pamięci, mogą wystąpić duże różnice. Widziałem przypadek, w którym wielowątkowa usługa systemu Windows skompilowana w trybie debugowania zużywała 700 MB na wątek, w porównaniu z 50 MB na wątek w kompilacji wydania. W kompilacji debugowania szybko zabrakło pamięci w typowych warunkach użytkowania.
o. nate
@Bengie - czy sprawdziłeś, że jeśli dołączysz debugger do kompilacji wydania, nadal działa on 2x szybciej? Zauważ, że powyższy cytat mówi, że na optymalizację JIT wpływa to, czy dołączony jest debugger.
ToolmakerSteve

Odpowiedzi:

100

Częściowo prawdziwe. W trybie debugowania kompilator emituje symbole debugowania dla wszystkich zmiennych i kompiluje kod bez zmian. W trybie wydania dostępne są pewne optymalizacje:

  • nieużywane zmienne w ogóle nie są kompilowane
  • niektóre zmienne pętli są pobierane z pętli przez kompilator, jeśli okaże się, że są niezmiennicze
  • kod napisany pod dyrektywą #debug nie jest uwzględniany itp.

Reszta należy do zespołu JIT.

Pełna lista optymalizacji tutaj dzięki uprzejmości Erica Lipperta .

Adrian Zanescu
źródło
10
I nie zapomnij o Debug.Asserts! W kompilacji DEBUG, jeśli się nie powiedzie, zatrzymają wątek i wyświetlą okno komunikatu. W wersji w ogóle nie są kompilowane. Dotyczy to wszystkich metod, które mają [ConditionalAttribute].
Ivan Zlatanov
13
Kompilator C # nie wykonuje optymalizacji połączeń końcowych; jitter robi. Jeśli chcesz dokładną listę tego, co robi kompilator C #, gdy przełącznik znajduje się na optymalizacji, zobacz blogs.msdn.com/ericlippert/archive/2009/06/11/...
Eric Lippert
64

Nie ma artykułu, który „udowadnia” cokolwiek na temat kwestii wydajności. Sposobem na udowodnienie twierdzenia o wpływie zmiany na wydajność jest wypróbowanie jej w obie strony i przetestowanie w realistycznych, ale kontrolowanych warunkach.

Zadajesz pytanie o wydajność, więc wyraźnie zależy ci na wydajności. Jeśli zależy Ci na wydajności, właściwą rzeczą jest ustalenie pewnych celów wydajnościowych, a następnie napisanie sobie zestawu testów, który śledzi Twoje postępy w realizacji tych celów. Kiedy już masz taki zestaw testów, możesz go łatwo użyć do samodzielnego sprawdzenia prawdziwości lub fałszywości instrukcji, takich jak „kompilacja do debugowania jest wolniejsza”.

Co więcej, będziesz w stanie uzyskać znaczące wyniki. „Wolniej” jest bez znaczenia, ponieważ nie jest jasne, czy jest o jedną mikrosekundę wolniej, czy o dwadzieścia minut wolniej. Bardziej wymowne jest określenie „10% wolniej w realistycznych warunkach”.

Poświęć czas, który spędziłbyś na badaniu tego pytania online, na zbudowaniu urządzenia, które odpowie na to pytanie. W ten sposób uzyskasz znacznie dokładniejsze wyniki. Wszystko, co czytasz w Internecie, to tylko przypuszczenie, co może się wydarzyć. Powód oparty na faktach, które sam zbierzesz, a nie na domysłach innych ludzi na temat zachowania twojego programu.

Eric Lippert
źródło
2
Myślę, że możesz dbać o wydajność, ale nadal chcesz używać funkcji „debugowania”. Na przykład, jeśli większość twojego czasu czeka na zależności, nie sądzę, aby budowanie w trybie debugowania miało duże znaczenie, ale masz dodatkową korzyść w postaci uzyskiwania numerów linii w śladach stosu, co może pomóc w szybszym naprawianiu błędów i szczęśliwsi użytkownicy. Chodzi o to, że musisz rozważyć zalety i wady, a ogólne stwierdzenie „debugowanie jest wolniejsze, ale tylko wtedy, gdy jesteś związany z procesorem”, wystarczy, aby pomóc w podjęciu decyzji.
Josh Mouch
11

Nie mogę komentować wydajności, ale rada „nie wdrażaj debugowania w środowisku produkcyjnym” jest nadal aktualna po prostu dlatego, że kod do debugowania zwykle robi całkiem inaczej w dużych produktach. Po pierwsze, możesz mieć aktywne przełączniki debugowania, a po drugie prawdopodobnie będą dodatkowe nadmiarowe kontrole poprawności i dane wyjściowe debugowania, które nie należą do kodu produkcyjnego.

Konrad Rudolph
źródło
Zgadzam się z tobą w tej kwestii, ale to nie odpowiada na główne pytanie
sagie
5
@sagie: tak, zdaję sobie z tego sprawę, ale pomyślałem, że warto było to zrobić.
Konrad Rudolph
6

Z msdn social

Nie jest to dobrze udokumentowane, oto co wiem. Kompilator emituje wystąpienie System.Diagnostics.DebuggableAttribute. W wersji do debugowania właściwość IsJitOptimizerEnabled ma wartość True, w wersji wydania ma wartość False. Ten atrybut można zobaczyć w manifeście zestawu za pomocą ildasm. Exe

Kompilator JIT używa tego atrybutu do wyłączania optymalizacji, które utrudniałyby debugowanie. Te, które przenoszą kod jak niezmienne podnoszenie pętli. W niektórych przypadkach może to mieć duży wpływ na wydajność. Jednak zwykle nie.

Odwzorowywanie punktów przerwania na adresy wykonywania jest zadaniem debugera. Wykorzystuje plik .pdb i informacje wygenerowane przez kompilator JIT, który dostarcza instrukcję IL do mapowania adresów kodu. Gdybyś napisał własny debugger, użyłbyś ICorDebugCode :: GetILToNativeMapping ().

Zasadniczo wdrażanie debugowania będzie wolniejsze, ponieważ optymalizacje kompilatora JIT są wyłączone.

Neil
źródło
3

To, co czytasz, jest całkiem aktualne. Wydanie jest zwykle bardziej oszczędne ze względu na optymalizację JIT, bez kodu debugowania (#IF DEBUG lub [Conditional ("DEBUG")]), minimalne ładowanie symboli debugowania i często nie jest brane pod uwagę to mniejszy zestaw, który skraca czas ładowania. Różnica w wydajności jest bardziej oczywista podczas uruchamiania kodu w VS ze względu na bardziej rozbudowany plik PDB i ładowane symbole, ale jeśli uruchomisz go niezależnie, różnice w wydajności mogą być mniej widoczne. Pewien kod będzie optymalizował się lepiej niż inne i używa tej samej heurystyki optymalizującej, jak w innych językach.

Scott ma dobre wytłumaczenie na rolkach optymalizacji metody tutaj

Zobacz ten artykuł, który zawiera krótkie wyjaśnienie, dlaczego różni się on w środowisku ASP.NET pod względem ustawień debugowania i wydania.

Fadrian Sudaman
źródło
3

Jedna rzecz, na którą należy zwrócić uwagę, dotycząca wydajności i tego, czy debugger jest podłączony, czy nie, coś, co nas zaskoczyło.

Mieliśmy fragment kodu obejmujący wiele ciasnych pętli, którego debugowanie wydawało się trwać wiecznie, a sam działał całkiem dobrze. Innymi słowy, żaden klient ani klient nie miał problemów, ale kiedy debugowaliśmy, wydawało się, że działa jak melasa.

Winowajcą był Debug.WriteLinejeden z ciasnych pętli, które wypluwają tysiące komunikatów dziennika, pozostawionych z sesji debugowania jakiś czas temu. Wygląda na to, że gdy debugger jest podłączony i nasłuchuje takiego wyjścia, wiąże się to z obciążeniem, które spowalnia program. W tym konkretnym kodzie czas działania był rzędu 0,2-0,3 sekundy samodzielnie i ponad 30 sekund, gdy debugger był dołączony.

Proste rozwiązanie, wystarczy usunąć komunikaty debugowania, które nie były już potrzebne.

Lasse V. Karlsen
źródło
2

W witrynie MSDN ...

Konfiguracje wydania a debugowania

Podczas gdy nadal pracujesz nad projektem, zwykle tworzysz aplikację przy użyciu konfiguracji debugowania, ponieważ ta konfiguracja umożliwia wyświetlanie wartości zmiennych i sterowanie wykonywaniem w debugerze. Możesz także tworzyć i testować kompilacje w konfiguracji wydania, aby upewnić się, że nie wprowadzono żadnych błędów, które pojawiają się tylko w jednym lub drugim typie kompilacji. W programowaniu .NET Framework takie błędy są bardzo rzadkie, ale mogą się pojawić.

Gdy będziesz gotowy do dystrybucji aplikacji do użytkowników końcowych, utwórz kompilację wydania, która będzie znacznie mniejsza i zwykle będzie miała znacznie lepszą wydajność niż odpowiednia konfiguracja debugowania. Konfigurację kompilacji można ustawić w okienku Kompilacja w Projektancie projektu lub na pasku narzędzi Kompilacja. Aby uzyskać więcej informacji, zobacz Konfiguracje budowania.

Hallie
źródło
2

Niedawno napotkałem problem z wydajnością. Pełna lista produktów zajmowała zbyt dużo czasu, około 80 sekund. Dostroiłem DB, poprawiłem zapytania i nie było żadnej różnicy. Postanowiłem stworzyć TestProject i dowiedziałem się, że ten sam proces został wykonany w 4 sekundy. Potem zdałem sobie sprawę, że projekt był w trybie debugowania, a projekt testowy był w trybie wydania. Przełączyłem główny projekt w tryb wydania, a pełna lista produktów zajęła tylko 4 sekundy, aby wyświetlić wszystkie wyniki.

Podsumowanie: tryb debugowania jest znacznie wolniejszy niż tryb uruchamiania, ponieważ zawiera informacje dotyczące debugowania. Zawsze powinieneś wdrażać w trybie wydania. Nadal możesz mieć informacje debugowania, jeśli dołączysz pliki .PDB. W ten sposób możesz na przykład rejestrować błędy z numerami linii.

Francisco Goldenstein
źródło
Przez „tryb pracy” masz na myśli „zwolnienie”?
Ron Klein,
Tak, dokładnie. Wydanie nie ma całego narzutu związanego z debugowaniem.
Francisco Goldenstein,
1

W dużej mierze zależy to od tego, czy Twoja aplikacja jest związana z obliczeniami i nie zawsze jest to łatwe do określenia, jak w przykładzie Lasse. Jeśli mam najmniejsze pytanie o to, co robi, zatrzymuję je kilka razy i sprawdzam stos. Jeśli dzieje się coś więcej, czego tak naprawdę nie potrzebowałem, to natychmiast to zauważa.

Mike Dunlavey
źródło
1

Tryby debugowania i wydania różnią się. Jest narzędzie Fuzzlyn : jest to fuzzer, który wykorzystuje Roslyn do generowania losowych programów C #. Uruchamia te programy na platformie .NET core i zapewnia, że ​​dają one takie same wyniki, gdy są kompilowane w trybie debugowania i wydania.

Dzięki temu narzędziu zostało znalezione i zgłoszone wiele błędów.

Sergey Ponomarev
źródło