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 ”.



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 .


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.


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.


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.


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.


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.


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.


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.


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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.