{"id":37786,"date":"2025-12-17T16:08:10","date_gmt":"2025-12-17T15:08:10","guid":{"rendered":"https:\/\/nearshore-it.eu\/?p=37786"},"modified":"2025-12-17T16:12:19","modified_gmt":"2025-12-17T15:12:19","slug":"optymalizacja-kodu-c-sharp-w-dot-net-techniki-poprawy-wydajnosci","status":"publish","type":"post","link":"https:\/\/nearshore-it.eu\/pl\/artykuly\/optymalizacja-kodu-c-sharp-w-dot-net-techniki-poprawy-wydajnosci\/","title":{"rendered":"Optymalizacja kodu C# w .NET &#8211; techniki poprawy wydajno\u015bci kodu aplikacji\u00a0"},"content":{"rendered":"\n<p>Je\u015bli masz wra\u017cenie, \u017ce krytyczny fragment kodu dzia\u0142a \u201etroch\u0119 za wolno\u201d, ten artyku\u0142 jest dla ciebie. Poka\u017c\u0119 na realnym przyk\u0142adzie z produkcji, jak optymalizacja kodu pozwoli\u0142a zej\u015b\u0107 z 10 do 3 maszyn w&nbsp;Azure&nbsp;&#8211; bez zmiany funkcjonalno\u015bci aplikacji. Zamiast teoretyzowa\u0107, przejdziemy przez konkretne techniki optymalizacji wydajno\u015bci kodu C#, kt\u00f3re mo\u017cesz od razu zastosowa\u0107 w swoim projekcie opartym o&nbsp;framework&nbsp;.NET.&nbsp;<\/p>\n\n\n\n<p>Dowiesz&nbsp;si\u0119:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>kiedy&nbsp;warto&nbsp;optymalizowa\u0107&nbsp;<\/li>\n\n\n\n<li>jak&nbsp;mierzy\u0107&nbsp;wydajno\u015b\u0107&nbsp;kodu&nbsp;<\/li>\n\n\n\n<li>jak dobra\u0107 algorytm i struktury danych, aby zwi\u0119kszy\u0107 wydajno\u015b\u0107 aplikacji,&nbsp;<\/li>\n\n\n\n<li>a tak\u017ce jak robi\u0107 to w spos\u00f3b, kt\u00f3ry nie zabija ca\u0142kowicie czytelno\u015bci kodu.&nbsp;<\/li>\n<\/ul>\n\n\n\n<div class=\"table-of-contents\">\n    <p class=\"title\"><\/p>\n    <ol>\n                    <li><a href=\"#wprowadzenie-od-10-do-3-maszyn-realna-historia-optymalizacji-kodu\">1.  Wprowadzenie:\u00a0od 10 do 3 maszyn: realna historia optymalizacji kodu<\/a><\/li>\n                    <li><a href=\"#kiedy-i-jak-przeprowadzac-optymalizacje-kodu\">2.  Kiedy i jak przeprowadza\u0107 optymalizacj\u0119 kodu?<\/a><\/li>\n                    <li><a href=\"#typy-optymalizacji\">3.  Typy optymalizacji<\/a><\/li>\n                    <li><a href=\"#ogolne-zasady-jak-pisac-wydajny-kod-kt\u00f3ry-nadal-da-si\u0119-utrzymac\">4.  Og\u00f3lne zasady &#8211; jak pisa\u0107 wydajny kod, kt\u00f3ry nadal da si\u0119 utrzyma\u0107<\/a><\/li>\n                    <li><a href=\"#faq\">5.  FAQ<\/a><\/li>\n            <\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"wprowadzenie-od-10-do-3-maszyn-realna-historia-optymalizacji-kodu\"><strong>Wprowadzenie:\u00a0od 10 do 3 maszyn: realna historia optymalizacji kodu<\/strong><\/h2>\n\n\n\n<p>Powodem powstania tego tekstu jest ch\u0119\u0107 podzielenia si\u0119 do\u015bwiadczeniem z optymalizacji kodu zdobytym podczas pracy nad prawdziw\u0105 aplikacj\u0105 w chmurze. Dwa lata temu by\u0142em cz\u0142onkiem zespo\u0142u, kt\u00f3ry mia\u0142 za zadanie programowa\u0107 system hostowany na platformie Microsoft&nbsp;Azure. System, zbudowany w oparciu o&nbsp;Azure&nbsp;Functions&nbsp;i .NET 7, przetwarza\u0142 strumie\u0144 zdarze\u0144 JSON:&nbsp;parsowanie, walidacja, budowa modeli &#8211; wszystko w czasie zbli\u017conym do rzeczywistego. Wym\u00f3g biznesowy by\u0142 prosty, ale wymagaj\u0105cy obliczeniowo: wydajno\u015b\u0107 aplikacji na poziomie 2000 zdarze\u0144 na sekund\u0119.&nbsp;<\/p>\n\n\n\n<p>Pierwsze\u00a0<a href=\"\/pl\/artykuly\/testy-niefunkcjonalne\/\" target=\"_blank\" rel=\"noreferrer noopener\">testy wydajno\u015bci<\/a>\u00a0pokaza\u0142y brutaln\u0105 prawd\u0119 &#8211; przy domy\u015blnej implementacji potrzebowali\u015bmy a\u017c 10 maszyn w dedykowanym planie\u00a0Azure. Po serii\u00a0mikrooptymalizacji\u00a0i ponownej analizie wydajno\u015bci okaza\u0142o si\u0119, \u017ce ten sam ruch obs\u0142u\u017cymy przy pomocy 3 maszyn o identycznych parametrach. To nie by\u0142a magia czy zas\u0142uga intuicji. To by\u0142 efekt \u015bwiadomego podej\u015bcia do zarz\u0105dzania pami\u0119ci\u0105, doboru algorytm\u00f3w, pracy na strukturach danych i mierzenia czasu wykonania.\u00a0<\/p>\n\n\n\n<p>Kod \u017ar\u00f3d\u0142owy przyk\u0142ad\u00f3w, benchmarki i wyniki dotycz\u0105ce wydajno\u015bci znajdziesz w repozytorium&nbsp;<a href=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/github-copilot-asystent-ai\/\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.&nbsp;<br><a href=\"https:\/\/github.com\/tomekjanicki\/Performance\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/tomekjanicki\/Performance<\/a>&nbsp;&#8211; ka\u017cdy benchmark w osobnym katalogu projektu Performance. Mo\u017cesz je samodzielnie uruchomi\u0107, aby sprawdzi\u0107 zachowanie na swojej maszynie, komend\u0105:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dotnet run -c Release --project .\\Performance\\Performance.csproj&amp;nbsp;-- --memory true&amp;nbsp;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"kiedy-i-jak-przeprowadzac-optymalizacje-kodu\"><strong>Kiedy i jak przeprowadza\u0107 optymalizacj\u0119 kodu?<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Istotn\u0105 kwesti\u0105 jest to, \u017ce optymalizacja kodu pod wzgl\u0119dem czasu wykonania lub zu\u017cycia pami\u0119ci&nbsp;ma sens tylko wtedy, gdy jak\u0105\u015b operacj\u0119 wykonujemy znacz\u0105c\u0105 ilo\u015b\u0107 razy&nbsp;w stosunkowo kr\u00f3tkim czasie i zale\u017cy nam na jak najkr\u00f3tszym czasie wykonania zadania przy jak najmniejszej konsumpcji zasob\u00f3w.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Generalnie tylko w tym przypadku nale\u017cy wykonywa\u0107 optymalizacj\u0119, gdy\u017c jednym z wa\u017cnych czynnik\u00f3w jest to, \u017ce kod po takiej optymalizacji jest zazwyczaj du\u017co mniej czytelny i trudniejszy do zrozumienia.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Do identyfikacji takich obszar\u00f3w kodu mo\u017cna u\u017cy\u0107 narz\u0119dzia typu&nbsp;profiler&nbsp;np.&nbsp;<a href=\"https:\/\/www.jetbrains.com\/dotmemory\" target=\"_blank\" rel=\"noopener\">https:\/\/www.jetbrains.com\/dotmemory<\/a>, <a href=\"https:\/\/www.jetbrains.com\/profiler\" target=\"_blank\" rel=\"noopener\">https:\/\/www.jetbrains.com\/profiler<\/a>&nbsp;lub&nbsp;narz\u0119dzi&nbsp;dostarczanych z Visual Studio. Maj\u0105c zidentyfikowane takie fragmenty kodu, mo\u017cna przyst\u0105pi\u0107 do optymalizacji.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Optymalizacja polega na pr\u00f3bie napisania kodu realizuj\u0105cego t\u0119 sam\u0105 funkcjonalno\u015b\u0107 w spos\u00f3b bardziej wydajny i&nbsp;por\u00f3wnaniu&nbsp;obydwu implementacji pod wzgl\u0119dem czasu wykonania i zu\u017cycia pami\u0119ci.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Narz\u0119dziem,&nbsp;kt\u00f3re&nbsp;staje si\u0119 de facto standardem, je\u015bli chodzi o pomiar wydajno\u015bci kodu przy przeprowadzaniu&nbsp;mikrooptymalizacji&nbsp;na platformie .NET, jest&nbsp;<a href=\"https:\/\/benchmarkdotnet.org\" target=\"_blank\" rel=\"noopener\">BenchmarkDotNet<\/a> &#8211; jest to narz\u0119dzie,&nbsp;kt\u00f3rego&nbsp;Microsoft wewn\u0119trznie u\u017cywa do monitoringu wydajno\u015bci podczas tworzenia samej platformy .NET. Narz\u0119dzie to jest wpi\u0119te w proces CI, kt\u00f3re monitoruje, czy okre\u015blone fragmenty kodu nie s\u0105 mniej wydajne po wprowadzonych zmianach. Je\u015bli wydajno\u015b\u0107 kodu si\u0119 pogarsza, wtedy zmiany w takim kodzie s\u0105 ponownie sprawdzane.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Idealnie by\u0142oby te\u017c, gdyby testy by\u0142y przeprowadzane na dok\u0142adnie takiej samej maszynie, na jakiej uruchamiany jest kod produkcyjnie &#8211; tzn. chodzi o model procesora, ilo\u015b\u0107 pami\u0119ci i system operacyjny.&nbsp;W przesz\u0142o\u015bci zdarza\u0142o si\u0119, \u017ce dana optymalizacja poprawia\u0142a wydajno\u015b\u0107 na danym \u015brodowisku, ale pogarsza\u0142a na innym &#8211; generalnie takie sytuacje s\u0105 stosunkowo rzadkie.&nbsp;&nbsp;<\/p>\n\n\n\n<p>W przypadku ka\u017cdego testu b\u0119dzie prezentowana tabela z wynikami &#8211; w przedstawionych rezultatach test\u00f3w najbardziej b\u0119d\u0105 istotne kolumny&nbsp;<strong>Mean&nbsp;<\/strong>oznaczaj\u0105ce czas wykonania i&nbsp;<strong>Allocated&nbsp;<\/strong>oznaczaj\u0105ce zu\u017cycie pami\u0119ci.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Podsumowuj\u0105c &#8211; w praktyce warto optymalizowa\u0107, gdy:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ten sam algorytm wykonujesz setki tysi\u0119cy lub miliony razy,&nbsp;<\/li>\n\n\n\n<li>fragment jest w krytycznym hot-path&nbsp;(np. wewn\u0105trz w\u0105skiej p\u0119tli przetwarzania),&nbsp;<\/li>\n\n\n\n<li>dotyczy intensywnej komunikacji z bazy danych, API albo serwer musi utrzyma\u0107 bardzo du\u017c\u0105 liczb\u0119 zapyta\u0144,&nbsp;<\/li>\n\n\n\n<li>czujesz, \u017ce szybko\u015b\u0107 i efektywno\u015b\u0107 s\u0105 kluczowe dla do\u015bwiadczenia u\u017cytkownika (np. czas \u0142adowania ekranu,&nbsp;responsywno\u015b\u0107&nbsp;panelu).&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>W innych przypadkach lepiej skupi\u0107 si\u0119 na czytelno\u015bci kodu i jako\u015bci projektu&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"typy-optymalizacji\"><strong>Typy optymalizacji<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Jak ju\u017c wcze\u015bniej wspomnia\u0142em, artyku\u0142 rozpatruje dwa typy optymalizacji.&nbsp;W pierwszym celem jest skr\u00f3cenie czasu wykonania, a w drugim &#8211; zmniejszenie konsumpcji pami\u0119ci.&nbsp;&nbsp;<\/p>\n\n\n\n<p>W wielu przypadkach&nbsp;optymalizacja zu\u017cycia pami\u0119ci przynosi znacznie bardziej spektakularne rezultaty (po\u015brednio te\u017c wp\u0142ywaj\u0105c na czas wykonania), gdy\u017c pami\u0119\u0107 jest \u0142atwo zaalokowa\u0107, ale proces zwalniania pami\u0119ci ze sterty jest du\u017co bardziej skomplikowany. Wi\u0119cej o dzia\u0142aniu mechanizmu&nbsp;Garbage&nbsp;Collector&nbsp;mo\u017cna znale\u017a\u0107 pod tym adresem&nbsp;<a href=\"https:\/\/www.youtube.com\/watch?v=BeuNvhd1L_g\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.youtube.com\/watch?v=BeuNvhd1L_g<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B01&nbsp;Logging&nbsp;&#8211; kiedy logi zabijaj\u0105 wydajno\u015b\u0107 aplikacji<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Logowanie (logging) mo\u017ce znacz\u0105co wp\u0142yn\u0105\u0107 na konsumpcj\u0119 zasob\u00f3w, zw\u0142aszcza gdy jest wykonywane setki tysi\u0119cy razy. Szczeg\u00f3\u0142owe wyniki benchmark\u00f3w dla tego scenariusza zosta\u0142y opublikowane w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B01Logging.LoggingBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B01 Logging \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>, do kt\u00f3rego b\u0119dziemy si\u0119 odnosi\u0107 w dalszej cz\u0119\u015bci sekcji.<\/p>\n\n\n\n<p>Przy por\u00f3wnywaniu wynik\u00f3w<strong>\u00a0<\/strong>wida\u0107, \u017ce gdy mamy w\u0142\u0105czony poziom logowania i fizycznie logujemy wiadomo\u015bci, to zu\u017cycie pami\u0119ci i czasy wykonania s\u0105 bardzo podobne (testy zaczynaj\u0105ce si\u0119 od\u00a0<code>LogInformationLevelEnabled<\/code>). Najwi\u0119ksze r\u00f3\u017cnice wida\u0107, gdy\u00a0wywo\u0142amy\u00a0metody loguj\u0105ce, ale poziom logowania nie jest w\u0142\u0105czony (testy zaczynaj\u0105ce si\u0119 od\u00a0<code>LogDebugLevelNotEnabled<\/code>). Wtedy najlepsze wyniki osi\u0105gamy, u\u017cywaj\u0105c trybu generowania kodu (<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">LogDebugLevelNotEnabledSourceGenerated<\/code>).\u00a0\u00a0<\/p>\n\n\n\n<p>Wi\u0119cej informacji:\u00a0<a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/core\/extensions\/logger-message-generator\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/learn.microsoft.com\/en-us\/dotnet\/core\/extensions\/logger-message-generator<\/a>. Najgorsze wyniki otrzymujemy w przypadku wykorzystania\u00a0mechanizmu string interpolacji (<code>LogDebugLevelNotEnabledStringInterpolation<\/code>) &#8211; ze wzgl\u0119du na natur\u0119 obiektu string, kt\u00f3ry jest niezmienny, za ka\u017cdym razem generujemy nowy string i alokujemy pami\u0119\u0107, nawet gdy w rzeczywisto\u015bci nic nie logujemy.\u00a0\u00a0<\/p>\n\n\n\n<p>Dodatkowo, gdy mamy na poziomie projektu (<code>Performance.csproj<\/code>) ustawiony poziom analizy kodu kompilatora na \u201elatest-recommended\u201d, to kompilator generuje ostrze\u017cenia CA1848:\u00a0Use\u00a0the\u00a0LoggerMessage\u00a0delegates\u00a0i CA2254:\u00a0Template\u00a0should\u00a0be a\u00a0static\u00a0expression. Wi\u0119cej o CA2254 mo\u017cna znale\u017a\u0107 pod tym adresem\u00a0<a href=\"https:\/\/www.youtube.com\/watch?v=6zoMd_FwSwQ\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.youtube.com\/watch?v=6zoMd_FwSwQ<\/a>\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B02&nbsp;Enums&nbsp;i generatory: szybsze&nbsp;TryParse&nbsp;\/&nbsp;ToString<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Wywo\u0142ywanie metod <code>TryParse<\/code> oraz <code>ToString<\/code> na obiektach typu wyliczeniowego wi\u0105\u017ce si\u0119 z u\u017cyciem refleksji, co ma bezpo\u015bredni wp\u0142yw na <strong>czas wykonania<\/strong> i zu\u017cycie pami\u0119ci. Zast\u0119puj\u0105c te metody alternatywnymi implementacjami opartymi o <strong>generowanie kodu<\/strong>, mo\u017cna znacz\u0105co poprawi\u0107 wydajno\u015b\u0107 \u2014 jednym z takich rozwi\u0105za\u0144 jest biblioteka <a href=\"https:\/\/www.nuget.org\/packages\/Supernova.Enum.Generators\" target=\"_blank\" rel=\"noopener\">Supernova.Enum.Generators<\/a>.<\/p>\n\n\n\n<p>R\u00f3\u017cnice s\u0105 wyra\u017anie widoczne w zestawieniu <strong>czasu wykonania oraz alokacji pami\u0119ci<\/strong> pomi\u0119dzy podej\u015bciem standardowym a wersj\u0105 generowan\u0105 (<code>Standard<\/code> vs <code>SourceGenerated<\/code>), co zosta\u0142o szczeg\u00f3\u0142owo pokazane w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B02Enums.EnumBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B02 Enums \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>\u0141atw\u0105 alternatyw\u0105 w przypadku metody&nbsp;<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ToString<\/code>&nbsp;jest zbudowanie przy starcie aplikacji statycznego s\u0142ownika i pobieranie warto\u015bci tekstowej na postawie warto\u015bci&nbsp;<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">enum<\/code>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B03&nbsp;Closures: ukryty wr\u00f3g w lambdach<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W przypadku wyra\u017ce\u0144 lambda sposobem na popraw\u0119 wydajno\u015bci jest unikanie <em>closures<\/em> (wi\u0119cej o <em>closures<\/em> w artykule <a href=\"https:\/\/csharpindepth.com\/Articles\/Closures\" target=\"_blank\" rel=\"noopener\"> C# in Depth<\/a>) poprzez przekazywanie zewn\u0119trznego stanu do \u015brodka funkcji lambda jako parametr.<\/p>\n\n\n\n<p>Na podstawie testu (<code>ExecuteWithoutClosure<\/code>) wida\u0107, \u017ce nast\u0119puje znacz\u0105ce skr\u00f3cenie czasu wykonania oraz nie wyst\u0119puje alokacja pami\u0119ci.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y opisane w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B03Closures.ClosuresBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B03 Closures \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>To dobry przyk\u0142ad na to, jak drobna zmiana stylu pisania kodu mo\u017ce pom\u00f3c w optymalizacji kodu bez wi\u0119kszej utraty jego czytelno\u015bci.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B04&nbsp;Spans&nbsp;&lt;T&gt;: przyspieszenie&nbsp;parser\u00f3w&nbsp;i praca \u201ena kraw\u0119dzi\u201d pami\u0119ci<\/strong>&nbsp;<\/h2>\n\n\n\n<p>U\u017cycie obiekt\u00f3w typu <em>span<\/em> (wi\u0119cej o <em>span<\/em> w artykule <a href=\"https:\/\/learn.microsoft.com\/en-us\/archive\/msdn-magazine\/2018\/january\/csharp-all-about-span-exploring-a-new-net-mainstay\" target=\"_blank\" rel=\"noopener\">C# \u2013 All About Span: Exploring a New .NET Mainstay<\/a>) znacz\u0105co przy\u015bpiesza dzia\u0142anie kodu, redukuj\u0105c potrzeb\u0119 dodatkowych alokacji pami\u0119ci. Jest on szczeg\u00f3lnie przydatny we wszelkiego rodzaju parserach, kt\u00f3re wyci\u0105gaj\u0105 dane z istniej\u0105cego obiektu, tworz\u0105c wirtualne okno w pami\u0119ci w przetwarzanym obiekcie, zapobiegaj\u0105c tym samym nowym alokacjom.<\/p>\n\n\n\n<p>Obydwa testy realizuj\u0105 identyczn\u0105 funkcjonalno\u015b\u0107, przetwarzaj\u0105c tekst na list\u0119 z warto\u015bciami typu liczbowego. Jak wida\u0107, test <code>GetResultSpan<\/code> jest znacz\u0105co szybszy i alokuje mniej pami\u0119ci w por\u00f3wnaniu do testu <code>GetResultClassic<\/code>.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B04Spans.SpanBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B04 Spans \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>Je\u015bli twoja aplikacja intensywnie parsuje tekst (np. logi, CSV, payloady z API), to u\u017cycie span\u00f3w mo\u017ce znacz\u0105co wp\u0142yn\u0105\u0107 na popraw\u0119 wydajno\u015bci.<\/p>\n\n\n\n<p>W&nbsp;benchmarkach:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>klasyczna implementacja alokuje nowe tablice\/stringi,&nbsp;<\/li>\n\n\n\n<li>implementacja ze&nbsp;spanami&nbsp;jest znacz\u0105co szybsza i zu\u017cywa mniej pami\u0119ci.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>To ju\u017c\u00a0dotyczy\u00a0zaawansowanych technik, ale w\u00a0parserach, systemach na\u00a0<a href=\"\/azure-iot-edge-computing\/\" target=\"_blank\" rel=\"noreferrer noopener\">edge<\/a>\u00a0(np.\u00a0IoT, funkcje\u00a0serverless) i us\u0142ugach API r\u00f3\u017cnica bywa kolosalna.\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B05&nbsp;Static&nbsp;&#8211;&nbsp;pola statyczne zamiast ci\u0105g\u0142ego tworzenia kolekcji<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Kolejnym przyk\u0142adem jest kod, kt\u00f3ry np. sprawdza bie\u017c\u0105c\u0105 warto\u015b\u0107 na podstawie niezmiennej listy warto\u015bci. Gdy ta lista jest niezmienna, warto zadeklarowa\u0107 j\u0105 na poziomie klasy jako pole statyczne i zamiast za ka\u017cdym razem tworzy\u0107 now\u0105 list\u0119, przekazywa\u0107 t\u0119 zadeklarowan\u0105 instancj\u0119 do wywo\u0142ania funkcji sprawdzaj\u0105cej.<\/p>\n\n\n\n<p>Jak wida\u0107, test <code>GetValidValuesStatic<\/code> znacz\u0105co obni\u017ca czas wykonania i zu\u017cycie pami\u0119ci w por\u00f3wnaniu do <code>GetValidValuesNotStatic<\/code>.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B05Static.StaticBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B05 Static \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B06&nbsp;Capacity&nbsp;&#8211;&nbsp;jak jedna linijka mo\u017ce znacznie poprawi\u0107 szybko\u015b\u0107 listy<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Przy dodawaniu element\u00f3w do obiektu typu generyczna lista dobrym zaleceniem jest ustawienie docelowego rozmiaru listy (je\u015bli znamy albo w przybli\u017ceniu jeste\u015bmy w stanie okre\u015bli\u0107 docelowy rozmiar listy). Ustawienie to wp\u0142ywa na to, \u017ce wewn\u0119trznie tablica, kt\u00f3ra przechowuje elementy, nie jest wielokrotnie realokowana i kod dzia\u0142a zauwa\u017calnie szybciej (<code>ProcessWithCapacity<\/code> vs <code>ProcessWithoutCapacity<\/code>).<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B06Capacity.CapacityBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B06 Capacity \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>To jedna z tych drobnych zmian, kt\u00f3re w intensywnych strukturach danych (np. listy obiekt\u00f3w DTO) robi\u0105 du\u017c\u0105 r\u00f3\u017cnic\u0119.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B07 Linq vs Dictionary&nbsp;i&nbsp;FrozenDictionary<\/strong><\/h2>\n\n\n\n<p>Nast\u0119pnym przyk\u0142adem jest sytuacja, w kt\u00f3rej wyszukujemy elementy po okre\u015blonych unikalnych kluczach. Je\u015bli dane, na kt\u00f3rych wyszukujemy, s\u0105 niezmienne lub wielokrotnie przeprowadzamy wyszukiwanie na tych samych danych, to op\u0142aca si\u0119 zamiast u\u017cywa\u0107 LINQ (<code>GetUsersByIdsLinq<\/code>) zbudowa\u0107 s\u0142ownik i przeprowadza\u0107 wyszukiwanie w tym s\u0142owniku (<code>GetUsersByIdsAlreadyBuiltDictionary<\/code>).<\/p>\n\n\n\n<p>Natomiast w sytuacji, gdy s\u0142ownik musi zosta\u0107 najpierw utworzony (<code>GetUsersByIdsBuildDictionary<\/code>), operacja ta jest znacznie bardziej czasoch\u0142onna i alokuje wi\u0119cej pami\u0119ci. Dodatkowo do por\u00f3wnania zosta\u0142 do\u0142\u0105czony niedawno wprowadzony typ <em>Frozen Dictionary<\/em> (wi\u0119cej informacji w dokumentacji <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/api\/system.collections.frozen.frozendictionary-2?view=net-9.0\" target=\"_blank\" rel=\"noopener\">FrozenDictionary \u2013 Microsoft Learn<\/a>), kt\u00f3ry \u2014 jak wida\u0107 \u2014 konsumuje jeszcze wi\u0119cej zasob\u00f3w podczas budowy (<code>GetUsersByIdsBuildFrozenDictionary<\/code>), ale jest minimalnie szybszy przy odczycie (<code>GetUsersByIdsAlreadyBuiltFrozenDictionary<\/code>).<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie<a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B07LinqVsDictionary.LinqVsDictionaryBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\"> B07 LINQ vs Dictionary \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>Je\u017celi dane, np. z bazy danych, s\u0105 \u0142adowane rzadko, a odczyty s\u0105 bardzo cz\u0119ste, to jest to idealny kandydat na zoptymalizowany kod z wykorzystaniem s\u0142ownika.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B08 Interface vs&nbsp;Implementation<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W pewnych przypadkach sposobem na optymalizacj\u0119 jest implementacja kodu za pomoc\u0105 obiektu typu warto\u015bciowego zamiast\u00a0<a href=\"file:\/\/\/C:\/pl\/artykuly\/c-11-sprawdz-co-nowego\/\" target=\"_blank\" rel=\"noreferrer noopener\">obiektu referencyjnego<\/a>\u00a0(<code>GetStructResultDirectly<\/code>) &#8211; dzi\u0119ki temu zabiegowi mo\u017cemy w pewnych sytuacjach zminimalizowa\u0107 dodatkowe alokacje pami\u0119ci. Jednak nale\u017cy zwr\u00f3ci\u0107 uwag\u0119, \u017ce nie nale\u017cy tego obiektu przekazywa\u0107 jako interfejs (<code>GetStructResultAsInterface<\/code>)\u00a0(gdy\u017c wymusza to operacj\u0119 <em>boxing<\/em>). W przypadku klasy nie ma to praktycznie \u017cadnego znaczenia.\u00a0<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B08InterfaceVsImplementation.InterfaceVsImplementationBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B08 Interface vs Implementation \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>Czasem zastosowanie\u00a0<code>record\u00a0struct<\/code>\u00a0zamiast klasy pozwala eliminowa\u0107 cz\u0119\u015b\u0107 alokacji.\u00a0Warunek:\u00a0<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>nie przekazujesz struktury jako interfejsu (boxing zwi\u0119ksza koszt),&nbsp;<\/li>\n\n\n\n<li>struktura jest u\u017cywana w algorytmie intensywnie przeliczaj\u0105cym dane.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>W przeciwnym razie&nbsp;przeci\u0105\u017cenie mentalne&nbsp;i ryzyko napisania b\u0142\u0119dnych konstrukcji jest wi\u0119ksze ni\u017c korzy\u015bci.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B09 List&nbsp;Manipulations&nbsp;zakresy zamiast pojedynczych operacji<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Kolejn\u0105 istotn\u0105 rzecz\u0105 w przypadku pracy z klas\u0105 typu generyczna lista jest efektywno\u015b\u0107 dodawania lub usuwania element\u00f3w z listy.<\/p>\n\n\n\n<p>Jest to szczeg\u00f3lnie wa\u017cne, gdy programista dodaje lub usuwa zbi\u00f3r element\u00f3w na pocz\u0105tku listy. Wida\u0107 wyra\u017anie, \u017ce dodawanie (<code>InsertItemsAtTheBeginningOneByOne<\/code>) lub usuwanie (<code>RemoveItemsAtTheBeginningOneByOne<\/code>) element\u00f3w pojedynczo jest najmniej efektywne \u2014 zwi\u0105zane jest to z wielokrotn\u0105 realokacj\u0105 wewn\u0119trznej tablicy obiektu typu list. Warto wtedy skorzysta\u0107 z metod operuj\u0105cych na zakresach (<code>InsertItemsAtTheBeginningByRange<\/code>, <code>RemoveItemsAtTheBeginningByRange<\/code>) lub zbudowa\u0107 ca\u0142kowicie now\u0105 list\u0119, korzystaj\u0105c z LINQ (<code>RemoveItemsAtTheBeginningByLinq<\/code>).<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie<a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B09ListManipulations.ListManipulationsBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\"> B09 List Manipulations \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B10&nbsp;Stack&nbsp;Alloc&nbsp;Array&nbsp;Pool&nbsp;&#8211;&nbsp;zarz\u0105dzanie pami\u0119ci\u0105 dla wymagaj\u0105cych<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Tworz\u0105c kod, kt\u00f3ry intensywnie operuje na ma\u0142ych tablicach, warto rozwa\u017cy\u0107 alokowanie element\u00f3w tej tablicy na stosie zamiast na stercie.<\/p>\n\n\n\n<p>Warto u\u017cy\u0107 wyra\u017cenia <code>stackalloc<\/code> (wi\u0119cej informacji w dokumentacji <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/operators\/stackalloc\" target=\"_blank\" rel=\"noopener\">stackalloc \u2013 Microsoft Learn<\/a>), maj\u0105c na uwadze ograniczenia stosu. W przypadku gdy potrzebujemy u\u017cy\u0107 tablicy o wi\u0119kszym rozmiarze, mo\u017cna skorzysta\u0107 z obiektu <code>ArrayPool<\/code> (zob. <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/api\/system.buffers.arraypool-1?view=net-9.0\" target=\"_blank\" rel=\"noopener\">ArrayPool&lt;T&gt; \u2013 Microsoft Learn<\/a>), kt\u00f3ry minimalizuje nowe alokacje pami\u0119ci, reu\u017cywaj\u0105c ju\u017c zaalokowan\u0105 pami\u0119\u0107.<\/p>\n\n\n\n<p>W tym konkretnym przyk\u0142adzie zosta\u0142a dwukrotnie zaimplementowana metoda wyliczaj\u0105ca hash z zawarto\u015bci obiektu \u2014 osobno dla ma\u0142ego i du\u017cego obiektu. Pierwsza implementacja jest klasyczna (<code>ExecuteHashCalculatorWithClassicSmallData<\/code>, <code>ExecuteHashCalculatorWithClassicLargeData<\/code>), a druga wykorzystuje <code>stackalloc<\/code> i array pool (<code>ExecuteHashCalculatorWithStackAllocOrArrayPoolSmallData<\/code>, <code>ExecuteHashCalculatorWithStackAllocOrArrayPoolLargeData<\/code>). Jak wida\u0107, zoptymalizowane metody konsumuj\u0105 du\u017co mniej pami\u0119ci i dzia\u0142aj\u0105 szybciej lub por\u00f3wnywalnie z klasyczn\u0105 implementacj\u0105.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie<a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B10StackAllocArrayPool.StackAllocArrayPoolBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\"> B10 StackAlloc &amp; ArrayPool \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>W&nbsp;przyk\u0142adach:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>klasyczny kod &#8211; alokuje nowe tablice przy ka\u017cdym wywo\u0142aniu,&nbsp;<\/li>\n\n\n\n<li>wersja ze&nbsp;stackalloc&nbsp;i&nbsp;ArrayPool&nbsp;&#8211; znacz\u0105co redukuje zu\u017cycia pami\u0119ci przy zachowaniu wysokiej szybko\u015bci dzia\u0142ania.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>To \u015bwietny przyk\u0142ad, jak \u015bwiadome zarz\u0105dzanie tablicami mo\u017ce odci\u0105\u017cy\u0107 zar\u00f3wno CPU, jak i GC.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B11&nbsp;Throwing&nbsp;Exceptions<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Je\u017celi chodzi o kod, kt\u00f3ry przeprowadza np. walidacj\u0119 danych wprowadzanych przez u\u017cytkownika, mamy dwa sposoby przekazywania u\u017cytkownikowi informacji, \u017ce wprowadzone dane nie s\u0105 zgodne z okre\u015blonymi regu\u0142ami. Pierwszym sposobem jest rzucanie wyj\u0105tk\u00f3w, a drugim zastosowanie wzorca <em>result<\/em>, w kt\u00f3rym przekazujemy informacj\u0119 o potencjalnych problemach. Por\u00f3wnuj\u0105c test <code>CreateUserWithResult<\/code> z <code>CreateUserWithException<\/code>, wida\u0107 spory narzut czasowy oraz alokacji pami\u0119ci w przypadku tego drugiego.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B11ThrowingExceptions.ThrowingExceptionsBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B11 Throwing Exceptions \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B12 Serialization JSON: UTF8,&nbsp;nie&nbsp;string<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W przypadku serializacji i deserializacji JSON przy u\u017cyciu <code>System.Text.Json<\/code> warto bazowa\u0107 bezpo\u015brednio na danych binarnych kodowanych do formatu UTF8, na kt\u00f3rym opiera si\u0119 komunikacja sieciowa (<code>SerializeDirectlyToUtf8<\/code>, <code>DeserializeDirectlyFromUtf8<\/code>), z pomini\u0119ciem transkodowania do obiektu typu <code>string<\/code> (<code>SerializeToStringToUtf8<\/code>, <code>DeserializeFromUtf8FromString<\/code>), kt\u00f3ry przechowuje dane w formacie UTF16. Por\u00f3wnuj\u0105c poni\u017csze testy, wida\u0107 znacz\u0105ce r\u00f3\u017cnice w zu\u017cyciu pami\u0119ci.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie<a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B12Serialization.SerializationBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\"> B12 Serialization \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B13 Dictionary&nbsp;Alternate&nbsp;Lookup<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W przypadku wyszukiwania w s\u0142owniku mo\u017cna skorzysta\u0107 z mechanizmu <em>Dictionary alternate lookup<\/em>, umo\u017cliwiaj\u0105cego przeszukiwanie s\u0142ownika przy u\u017cyciu klucza innego typu ni\u017c ten, z kt\u00f3rym zosta\u0142 on pierwotnie utworzony. Jest to mo\u017cliwe dzi\u0119ki strukturze <code>AlternateLookup<\/code>, kt\u00f3ra u\u017cywa klasy <code>IAlternateEqualityComparer<\/code> do por\u00f3wnywania r\u00f3\u017cnych typ\u00f3w kluczy, co pozwala na popraw\u0119 wydajno\u015bci \u2014 na przyk\u0142ad wyszukiwanie danych za pomoc\u0105 klasy <code>ReadOnlySpan&lt;char&gt;<\/code> (<code>CountWords2<\/code>) zamiast przydzielania nowego ci\u0105gu znak\u00f3w (<code>CountWords1<\/code>). W tym przyk\u0142adzie wida\u0107 ogromne r\u00f3\u017cnice w konsumpcji pami\u0119ci.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B13DictionaryAlternateLookup.DictionaryAlternateLookupBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B13 Dictionary Alternate Lookup \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B14&nbsp;Structs<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W przypadku definiowania nowych obiekt\u00f3w typu warto\u015bciowego zalecanym sposobem definiowania tego typu obiekt\u00f3w jest korzystanie z konstrukcji <code>record struct<\/code> zamiast <code>struct<\/code>. Ten pierwszy ma generowane przez kompilator metody <code>GetHashCode<\/code> i <code>ToString<\/code>. Standardowy <code>struct<\/code> u\u017cywa refleksji do implementacji tych metod, kt\u00f3ra jest du\u017co mniej efektywna. Jest to szczeg\u00f3lnie wa\u017cne, je\u017celi tak zdefiniowanej struktury u\u017cywamy jako klucza w s\u0142owniku lub korzystamy z obiektu typu <code>HashSet<\/code>. Metoda <code>GetDictionaryKeyAsRecordStruct<\/code> dzia\u0142a du\u017co efektywniej ni\u017c <code>GetDictionaryKeyAsStruct<\/code>.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie <a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B14Structs.StructsBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\">B14 Structs \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>B15&nbsp;Streams<\/strong>&nbsp;<\/h2>\n\n\n\n<p>W przypadku przetwarzania du\u017cych zbior\u00f3w danych zamiast klasycznego przetwarzania wszystkich danych w pami\u0119ci warto rozwa\u017cy\u0107 zastosowanie obiekt\u00f3w typu <em>stream<\/em> oraz <code>I(Async)Enumerable<\/code>. Zaprezentowany przyk\u0142ad realizuje funkcjonalno\u015b\u0107 czytania z pliku ka\u017cdego wiersza, zamiany go na obiekt, wyliczenia warto\u015bci \u015bredniej i zapisania wyliczonej warto\u015bci \u015bredniej wraz z oryginalnymi warto\u015bciami w pliku wynikowym.<\/p>\n\n\n\n<p>W pierwszej implementacji (<code>AsyncProcessingGetRowsWriteRows<\/code>) wszystkie wiersze \u017ar\u00f3d\u0142owe s\u0105 wczytywane do pami\u0119ci, a nast\u0119pnie ca\u0142a kolekcja wiersz po wierszu zapisywana jest w pliku wynikowym. W drugiej implementacji (<code>AsyncStreamProcessingReWrite<\/code>) naraz pobierany jest tylko jeden wiersz, wykonywane s\u0105 obliczenia na tym wierszu, a wynik zapisywany jest bezpo\u015brednio w pliku wynikowym. Z por\u00f3wnania wynik\u00f3w wida\u0107, \u017ce pierwsza implementacja jest minimalnie szybsza, ale zu\u017cywa du\u017co wi\u0119cej pami\u0119ci.<\/p>\n\n\n\n<p>Szczeg\u00f3\u0142owe wyniki benchmarku zosta\u0142y przedstawione w raporcie<a href=\"https:\/\/github.com\/tomekjanicki\/Performance\/blob\/master\/Performance\/results\/Performance.B15Streams.StreamsBenchmark-report-github.md\" target=\"_blank\" rel=\"noopener\"> B15 Streams \u2013 pe\u0142ne wyniki benchmark\u00f3w<\/a>.<\/p>\n\n\n\n<p>Je\u015bli tw\u00f3j system pracuje na du\u017cych plikach, logach lub danych z bazy danych, strumieniowe przetwarzanie pozwoli utrzyma\u0107 stabiln\u0105 wydajno\u015b\u0107 twoich aplikacji, nawet gdy wolumen danych ro\u015bnie.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ogolne-zasady-jak-pisac-wydajny-kod-kt\u00f3ry-nadal-da-si\u0119-utrzymac\"><strong>Og\u00f3lne zasady &#8211; jak pisa\u0107 wydajny kod, kt\u00f3ry nadal da si\u0119 utrzyma\u0107<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Na koniec kilka zasad, kt\u00f3re warto zapami\u0119ta\u0107, je\u015bli chcesz zwi\u0119kszy\u0107 wydajno\u015b\u0107 aplikacji, ale nadal spa\u0107 spokojnie:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Zaczynaj od algorytmu.&nbsp;<\/strong>Dobry algorytm i odpowiednie struktury danych cz\u0119sto daj\u0105 wi\u0119cej ni\u017c lokalne&nbsp;mikrooptymalizacje.&nbsp;<\/li>\n\n\n\n<li><strong>Mierz, nie zgaduj. U\u017cywaj narz\u0119dzi<\/strong>&nbsp;&#8211; Visual Studio,&nbsp;BenchmarkDotNet, log\u00f3w &#8211; aby mierzy\u0107 realne problemy.&nbsp;<\/li>\n\n\n\n<li><strong>My\u015bl o ca\u0142o\u015bci<\/strong>&nbsp;&#8211; od bazy po UI. To, jak kod &#8222;rozmawia&#8221; z baz\u0105 danych, jak obs\u0142uguje API, jak dzia\u0142a po stronie serwera i jak zarz\u0105dza pami\u0119ci\u0105, ma wp\u0142yw na ca\u0142e do\u015bwiadczenie u\u017cytkownika.&nbsp;<\/li>\n\n\n\n<li><strong>Korzystaj&nbsp;z&nbsp;pomocy spo\u0142eczno\u015bci&nbsp;i dokumentacji.&nbsp;<\/strong>Dokumentacja Microsoft, kursy Microsoft&nbsp;Learn, repozytoria GitHub i przyk\u0142ady&nbsp;kodu&nbsp;innych zespo\u0142\u00f3w to \u015bwietne dodatkowe zasoby, kt\u00f3re mog\u0105 pokaza\u0107 ci gotowe techniki optymalizacji.&nbsp;<\/li>\n\n\n\n<li><strong>Pami\u0119taj o u\u017cytkowniku.<\/strong>&nbsp;Dla biznesu liczy si\u0119 kr\u00f3tszy czas odpowiedzi, mniejsze koszty utrzymania, mniej maszyn, stabilno\u015b\u0107 dzia\u0142ania. Techniczne detale s\u0142u\u017c\u0105 temu, by dowie\u017a\u0107 lepsz\u0105 wydajno\u015b\u0107 i realne korzy\u015bci.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Dobrze zaprojektowany, wydajny i efektywny kod C# nie musi by\u0107 \u201emagiczny\u201d i niezrozumia\u0142y. Mo\u017cna \u0142\u0105czy\u0107 pragmatyzm z jako\u015bci\u0105 &#8211; tak, aby zwi\u0119kszy\u0107 wydajno\u015b\u0107 aplikacji tam, gdzie to naprawd\u0119 ma znaczenie, a jednocze\u015bnie zachowa\u0107 sensown\u0105 czytelno\u015b\u0107 dla kolejnych os\u00f3b, kt\u00f3re b\u0119d\u0105 ten kod rozwija\u0107 i programowa\u0107 w nim nowe funkcje.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq\">FAQ&nbsp;<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1765977988901\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \">Co to jest optymalizacja kodu .NET i dlaczego optymalizacja kodu to wa\u017cny element?\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Optymalizacja kodu .NET to proces poprawiania szybko\u015bci, zu\u017cycia pami\u0119ci i og\u00f3lnej efektywno\u015bci wykonywania kodu w aplikacjach opartych na .NET. Optymalizacja kodu to wa\u017cny aspekt tworzenia oprogramowania, poniewa\u017c wp\u0142ywa na do\u015bwiadczenie u\u017cytkownika, koszty infrastruktury i stabilno\u015b\u0107 systemu. Przy optymalizacji warto\u00a0uwzgl\u0119dni\u0107 silnik uruchomieniowy .NET, bibliotek\u0119 u\u017cywan\u0105 przez tw\u00f3j kod oraz unika\u0107 przedwczesnej optymalizacji, kt\u00f3ra mo\u017ce skomplikowa\u0107 implementacj\u0119 bez realnych korzy\u015bci.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765978013395\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Jakie narz\u0119dzia w Visual Studio pomagaj\u0105 mierzy\u0107 wydajno\u015b\u0107 kodu?<\/strong>\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Visual Studio oferuje\u00a0profiler,\u00a0diagnosery\u00a0i narz\u0119dzia do mierzenia wydajno\u015bci, kt\u00f3re pozwalaj\u0105 \u015bledzi\u0107 czas wykonywania kodu, zu\u017cycie pami\u0119ci i<strong>\u00a0<\/strong>liczb\u0119 utworzonych\u00a0objects.\u00a0Dzi\u0119ki tym narz\u0119dziom mo\u017cesz znale\u017a\u0107 w\u0105skie gard\u0142a, por\u00f3wna\u0107 r\u00f3\u017cne implementacje i zdecydowa\u0107, jakie zmiany przynios\u0105 najwi\u0119ksz\u0105 popraw\u0119. W praktyce przed optymalizacj\u0105 warto zmierzy\u0107\u00a0baseline, aby wiedzie\u0107, co rzeczywi\u015bcie wymaga poprawy.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765978030450\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Jak optymalizacja wp\u0142ywa na zu\u017cycie zasob\u00f3w i kiedy warto wy\u0142\u0105czy\u0107 pewne funkcje?<\/strong>\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Optymalizacja wp\u0142ywa bezpo\u015brednio na zu\u017cycie zasob\u00f3w takich jak pami\u0119\u0107 i CPU. Czasami warto wy\u0142\u0105czy\u0107 funkcje generuj\u0105ce nadmierne alokacje lub kosztowne operacje w tle, szczeg\u00f3lnie gdy obci\u0105\u017cenie jest wysokie. Przy optymalizacji twojego kodu nale\u017cy analizowa\u0107, czy bardziej op\u0142acalne b\u0119dzie zoptymalizowa\u0107 algorytm, zmieni\u0107 bibliotek\u0119 czy wy\u0142\u0105czy\u0107 rzadko u\u017cywane elementy, kt\u00f3re obci\u0105\u017caj\u0105 silnik wykonywania kodu.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765978063823\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Jak efektywnie mierzy\u0107 i analizowa\u0107 czas wykonywania kodu w .NET?<\/strong>\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Aby mierzy\u0107 czas wykonywania kodu, u\u017cyj wbudowanych\u00a0profiler\u00f3w, narz\u0119dzi do mierzenia wydajno\u015bci, logowania z\u00a0timestampami\u00a0lub bibliotek do benchmark\u00f3w (np.\u00a0BenchmarkDotNet). Zbieraj metryki w warunkach zbli\u017conych do produkcji i uwzgl\u0119dniaj warunki brzegowe. Unikaj przedwczesnej optymalizacji &#8211; najpierw zidentyfikuj\u00a0hotspoty\u00a0za pomoc\u0105 narz\u0119dzi do mierzenia wydajno\u015bci, a potem skup si\u0119\u00a0na tym, by\u00a0zoptymalizowa\u0107 fragmenty, kt\u00f3re rzeczywi\u015bcie wp\u0142ywaj\u0105 na wydajno\u015b\u0107.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765978085075\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Kiedy nale\u017cy zoptymalizowa\u0107 kod, a kiedy lepiej u\u017cy\u0107 gotowej biblioteki?<\/strong>\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Je\u015bli problem le\u017cy w algorytmie lub alokacjach, warto zoptymalizowa\u0107 kod samodzielnie, jednak cz\u0119sto gotowa biblioteka oferuje dobrze przetestowane i zoptymalizowane rozwi\u0105zania, kt\u00f3re oszcz\u0119dzaj\u0105 czas. Przy wyborze biblioteki sprawd\u017a jej wp\u0142yw na wykonywanie\u00a0kodu, kompatybilno\u015b\u0107 z silnikiem .NET i licencj\u0119.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765978101702\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Jak unikn\u0105\u0107 przedwczesnej optymalizacji i jakie praktyki stosowa\u0107?<\/strong>\u00a0<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Unikaj przedwczesnej optymalizacji &#8211;\u00a0skup si\u0119 w pierwszej kolejno\u015bci na\u00a0poprawno\u015bci i czytelno\u015bci kodu. Stosuj profilowanie i mierzenie wydajno\u015bci przed wprowadzaniem zmian. Praktyki takie jak\u00a0cache&#8217;owanie\u00a0wynik\u00f3w, minimalizowanie alokacji\u00a0objects, u\u017cywanie struktur i\u00a0span\u00f3w\u00a0w krytycznych sekcjach oraz asynchroniczne operacje IO pomagaj\u0105 tworzy\u0107 zoptymalizowany kod bez niepotrzebnego ryzyka.\u00a0<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Dowiedz si\u0119, jak praktyczna optymalizacja kodu C# w .NET \u2013 oparta na realnych benchmarkach \u2013 pozwoli\u0142a zmniejszy\u0107 liczb\u0119 maszyn z 10 do 3. Konkretne techniki, mierzalne wyniki i przyk\u0142ady, kt\u00f3re mo\u017cesz od razu zastosowa\u0107 w swoich aplikacjach.<\/p>\n","protected":false},"author":211,"featured_media":37812,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"iawp_total_views":28,"footnotes":""},"categories":[1,402,582],"tags":[],"offering":[522],"class_list":["post-37786","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artykuly","category-articles","category-technologie","offering-tech-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/37786","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/users\/211"}],"replies":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/comments?post=37786"}],"version-history":[{"count":7,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/37786\/revisions"}],"predecessor-version":[{"id":37822,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/37786\/revisions\/37822"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media\/37812"}],"wp:attachment":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media?parent=37786"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/categories?post=37786"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/tags?post=37786"},{"taxonomy":"offering","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/offering?post=37786"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}