{"id":31252,"date":"2020-04-29T06:19:36","date_gmt":"2020-04-29T04:19:36","guid":{"rendered":"https:\/\/nearshore-it.eu\/artykuly\/pulapki-wspolbieznosci-narzut-synchronizacji-watkow\/"},"modified":"2024-11-07T15:28:05","modified_gmt":"2024-11-07T14:28:05","slug":"pulapki-wspolbieznosci-narzut-synchronizacji-watkow","status":"publish","type":"post","link":"https:\/\/nearshore-it.eu\/pl\/artykuly\/pulapki-wspolbieznosci-narzut-synchronizacji-watkow\/","title":{"rendered":"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci. Narzut synchronizacji w\u0105tk\u00f3w"},"content":{"rendered":"\n<div class=\"table-of-contents\">\n    <p class=\"title\">Przejd\u017a do:<\/p>\n    <ol>\n                    <li><a href=\"#Punkt-wyjscia\">1.  Punkt wyj\u015bcia<\/a><\/li>\n                    <li><a href=\"#Pulapka-naiwnej-optymalizacji\">2.  Pu\u0142apka naiwnej optymalizacji<\/a><\/li>\n                    <li><a href=\"#Meandry-rownoleglosci\">3.  Meandry r\u00f3wnoleg\u0142o\u015bci<\/a><\/li>\n                    <li><a href=\"#Kiedy-zrownoleglac-obliczenia\">4.  Kiedy zr\u00f3wnolegla\u0107 obliczenia?<\/a><\/li>\n                    <li><a href=\"#Podsumowanie-warto-robic-pomiary\">5.  Podsumowanie: warto robi\u0107 pomiary<\/a><\/li>\n            <\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"Punkt-wyjscia\">Punkt wyj\u015bcia<\/h2>\n\n\n\n<p>Programowanie wsp\u00f3\u0142bie\u017cne jest tematem bardzo rozleg\u0142ym i zawiera w sobie wiele element\u00f3w, o kt\u00f3rych trzeba pami\u0119ta\u0107, aby dostosowa\u0107 spos\u00f3b jego u\u017cycia do potrzeb danego scenariusza i by jego u\u017cycie nie przynios\u0142o rezultat\u00f3w odwrotnych do zamierzonych. Ju\u017c sama historia ewolucji API .NETa (mechanizmy takie, jak instrukcja lock, semafory, interfejs IAsyncResult, zdarzenia, biblioteka TPL a\u017c po klas\u0119 ValueTask) pokazuje, \u017ce temat ten nie jest \u0142atwy nawet dla in\u017cynier\u00f3w Microsoftu. <strong>\u015awiadomo\u015b\u0107 niskopoziomowych mechanizm\u00f3w dzia\u0142aj\u0105cych pod t\u0105 technologi\u0105 u\u0142atwia prawid\u0142owe zastosowanie tych narz\u0119dzi.<\/strong> W tym artykule zostanie poruszony tylko niewielki wycinek tego obszaru wiedzy.<\/p>\n\n\n\n<p>Aby zobrazowa\u0107 pewne aspekty u\u017cycia programowania wsp\u00f3\u0142bie\u017cnego, wzi\u0105\u0142em za przyk\u0142ad scenariusz, w kt\u00f3rym chc\u0119 wyliczy\u0107 liczby pierwsze w zadanym zakresie liczb naturalnych. Aby sprawdzi\u0107, czy dana liczba jest liczb\u0105 pierwsz\u0105, pos\u0142u\u017cy\u0142em si\u0119 przyk\u0142adowym kodem <a href=\"https:\/\/stackoverflow.com\/a\/15743238\/4439713\" target=\"_blank\" rel=\"noopener\">znalezionym na StackOverflow<\/a>:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_1.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27414\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Rozpocz\u0105\u0142em od przetwarzania sekwencyjnego, by sprawdzi\u0107, ile czasu zajmuje wyliczenie liczb pierwszych w zakresie od 1 do 100. Aby zmierzy\u0107 ten czas, wykorzysta\u0142em <strong>bibliotek\u0119 BenchmarkDotNet.<\/strong><\/p>\n\n\n\n<p>W tym celu:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Stworzy\u0142em program konsolowy (w moim przypadku wykorzystam <strong>.NET Core 3.0<\/strong>),<\/li>\n\n\n\n<li>Doda\u0142em paczk\u0119 nuget <strong>BenchmarkDotNet<\/strong> (w moim przypadku jest to wersja 0.11.5),<\/li>\n\n\n\n<li>Doda\u0142em klas\u0119 <strong>BenchmarkTest<\/strong><\/li>\n\n\n\n<li>Do klasy <strong>BenchmarkTest<\/strong> doda\u0142em wylistowan\u0105 wy\u017cej metod\u0119 IsPrime,<\/li>\n\n\n\n<li>Do klasy <strong>BenchmarkTest<\/strong> doda\u0142em metod\u0119 wyliczania sekwencyjnego liczb pierwszych oraz opatrzy\u0142em t\u0119 metod\u0119 atrybutem <strong>[Benchmark]<\/strong> z przestrzeni nazw <strong>BenchmarkDotNet.Attributes:<\/strong><\/li>\n<\/ol>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_2.png\" alt=\" class=\" class=\"wp-image-27415\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Program musia\u0142 by\u0107 zbudowany w trybie <strong>RELEASE<\/strong>, aby da\u0107 wymierne wyniki. Aby wi\u0119c unikn\u0105\u0107 usuni\u0119cia powy\u017cszej metody podczas procesu optymalizacji, powinienem by\u0142 zadba\u0107, aby jej typ zwracany nie by\u0142 deklarowany jako void. Dlatego zdecydowa\u0142em, by zwraca\u0142a wyliczony wynik jako list\u0119 liczb pierwszych.<\/p>\n\n\n\n<ol start=\"6\" class=\"wp-block-list\">\n<li>Do metody statycznej <strong>Program.Main<\/strong> doda\u0142em wywo\u0142anie mechanizmu mierzenia wydajno\u015bci testowanej metody:<\/li>\n<\/ol>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_3.png\" alt=\" class=\" class=\"wp-image-27416\" title=\"\"><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ol start=\"7\" class=\"wp-block-list\">\n<li>Zmieni\u0142em konfiguracj\u0119 projektu na <strong>Release.<\/strong><\/li>\n<\/ol>\n\n\n\n<p>W dalszym kroku mog\u0142em ju\u017c uruchomi\u0107 projekt, ale \u2013 uwaga! \u2013 aby zmniejszy\u0107 wp\u0142yw innych proces\u00f3w na wyniki testu (np. trybu debuggowania z Visual Studio), zrobi\u0142em to z linii polece\u0144.<\/p>\n\n\n\n<p>W wyniku uruchomienia zosta\u0142y wykonane wielokrotne testy, a na ko\u0144cu otrzyma\u0142em tabelk\u0119 z takimi wynikami:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_4.png\" alt=\"Tabela - wp\u00f3\u0142bie\u017cno\u015b\u0107\" class=\"wp-image-27417\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Pulapka-naiwnej-optymalizacji\">Pu\u0142apka naiwnej optymalizacji<\/h2>\n\n\n\n<p>Nast\u0119pnie chcia\u0142em zmierzy\u0107 czas potrzebny na wygenerowanie tej samej listy liczb pierwszych w spos\u00f3b r\u00f3wnoleg\u0142y. W tym celu do klasy <strong>BenchmarkTest<\/strong> doda\u0142em odpowiednio zmodyfikowan\u0105 metod\u0119. Zamiast klasy <strong>Enumerable<\/strong> wykorzysta\u0142em ParallelEnumerable, kt\u00f3rego metoda Range generuje r\u00f3wnoleg\u0142\u0105 sekwencj\u0119 liczb (ang. parallel sequence of integral numbers):<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_5.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27418\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Po uruchomieniu programu narz\u0119dzie BenchmarkDotNet zmierzy\u0142o \u015bredni czas wykonania dla ka\u017cdej z obu metod.<\/p>\n\n\n\n<p>Wyniki by\u0142y nast\u0119puj\u0105ce:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_6.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27419\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Jak wida\u0107, <strong>zr\u00f3wnoleglone zadanie zosta\u0142o wykonane w czasie ok. 8 razy d\u0142u\u017cszym<\/strong> ni\u017c zadanie wykonane sekwencyjnie. Sk\u0105d to mo\u017ce wynika\u0107?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Meandry-rownoleglosci\">Meandry r\u00f3wnoleg\u0142o\u015bci<\/h2>\n\n\n\n<p>Microsoft na stronie <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/parallel-programming\/understanding-speedup-in-plinq\" target=\"_blank\" rel=\"noopener\">Understanding Speedup in PLINQ<\/a> wyja\u015bnia, \u017ce jedn\u0105 z przyczyn niepowodzenia optymalizacji przez zr\u00f3wnoleglenie jest zjawisko wyst\u0119powania pewnego narzutu czasu (ang. overhead) potrzebnego do synchronizacji zada\u0144 pomi\u0119dzy w\u0105tkami.<\/p>\n\n\n\n<p>To zjawisko mo\u017cna zaobserwowa\u0107, analizuj\u0105c zapis zdarze\u0144 systemowych, kt\u00f3re w systemie Windows funkcjonuj\u0105 pod nazw\u0105 <strong>Event Tracing for Windows.<\/strong> Aby zebra\u0107 i przeanalizowa\u0107 te zdarzenia, u\u017cy\u0142em narz\u0119dzia <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=Diagnostics.ConcurrencyVisualizer2017\" target=\"_blank\" rel=\"noopener\">Concurrency Visualizer for Visual Studio 2017<\/a>, kt\u00f3re jest dodatkiem do Visual Studio 2017 (w momencie pisania tego artyku\u0142u nie istnia\u0142 dodatek dla Visual Studio 2019).<\/p>\n\n\n\n<p>Aby u\u0142atwi\u0107 analiz\u0119 zdarze\u0144 generowanych przez Concurrency Visualizer, chwilowo zrezygnowa\u0142em z uruchamiania analizowanego kodu przez BenchmarkDotNet, a w zamian uruchomi\u0142em tylko raz jedn\u0105 z testowanych metod \u2013 najpierw wersj\u0119 sekwencyjn\u0105, potem wielow\u0105tkow\u0105. W tym celu zmodyfikowa\u0142em metod\u0119 Program.Main do poni\u017cszej postaci:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_7.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27420\" title=\"\"><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">Dodatkowo, aby pozwoli\u0107 narz\u0119dziu na odczytanie dany<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">ch diagnostycznych z uruchamianego procesu,&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">zmodyfikowa\u0142em<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">&nbsp;plik&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"SpellingError SCXW17250991 BCX0\" data-wac-het=\"1\">csproj<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">&nbsp;projektu, dodaj\u0105c&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">w nim&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">do&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">element\u00f3w<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">&nbsp;<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"SpellingError SCXW17250991 BCX0\" data-wac-het=\"1\">PropertyGroup<\/span><\/span><span class=\"TextRun SCXW17250991 BCX0\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW17250991 BCX0\" data-wac-het=\"1\">&nbsp;poni\u017cszy fragment XML:<\/span><\/span><span class=\"EOP SCXW17250991 BCX0\" data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\" data-wac-het=\"1\">&nbsp;<\/span><\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_8.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27421\" title=\"\"><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Po przebudowaniu aplikacji uruchomi\u0142em Visual Studio 2017 i z menu Analyze \/ Concurrency Visualizer wybra\u0142em komend\u0119 <strong>Launch New Process<\/strong>. W okienku z parametrami wskaza\u0142em \u015bcie\u017ck\u0119 do pliku exe mojej aplikacji:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"167\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b1.jpg\" alt=\"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci\" class=\"wp-image-32494\" title=\"\" srcset=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b1.jpg 1200w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b1-300x42.jpg 300w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b1-768x107.jpg 768w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b1-495x69.jpg 495w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Po klikni\u0119ciu przycisku <strong>Start<\/strong> zosta\u0142 uruchomiony m\u00f3j program, a nast\u0119pnie zosta\u0142y wczytane dane dotycz\u0105ce zdarze\u0144 zarejestrowanych podczas jego uruchomienia.<\/p>\n\n\n\n<p>Przed przeprowadzeniem tego procesu istotne jest, aby zamyka\u0107 wszystkie zb\u0119dne aplikacje, by ich dzia\u0142anie nie wp\u0142yn\u0119\u0142o na wynik analizy. Dodatkowo warto t\u0119 analiz\u0119 powt\u00f3rzy\u0107 kilka razy, aby sprawdzi\u0107, czy wyniki s\u0105 do siebie wystarczaj\u0105co zbli\u017cone, i w ten spos\u00f3b upewni\u0107 si\u0119 co do rzetelno\u015bci wynik\u00f3w. Ja na potrzeby tego artyku\u0142u ka\u017cd\u0105 analiz\u0119 wykona\u0142em po trzy razy i \u2013 por\u00f3wnuj\u0105c wyniki mi\u0119dzy sob\u0105 \u2013 uzna\u0142em t\u0119 liczb\u0119 powt\u00f3rze\u0144 za wystarczaj\u0105c\u0105.<\/p>\n\n\n\n<p>Po zako\u0144czeniu \u0142adowania zobaczy\u0142em taki wykres:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"497\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b2.jpg\" alt=\"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci\" class=\"wp-image-32506\" title=\"\" srcset=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b2.jpg 1200w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b2-300x124.jpg 300w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b2-768x318.jpg 768w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b2-495x205.jpg 495w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Przedstawia on stopie\u0144 wykorzystania rdzeni logicznych przez uruchomion\u0105 aplikacj\u0119 (kolor zielony) w czasie (o\u015b pozioma) analizy. Za pomoc\u0105 kolor\u00f3w szarych przedstawiony jest stopie\u0144 wykorzystania rdzeni logicznych przez pozosta\u0142e procesy. Kolor bia\u0142y przedstawia niewykorzystane zasoby procesora.<\/p>\n\n\n\n<p>Z punktu widzenia tej analizy istotniejsze dane kryj\u0105 si\u0119 jednak pod elementem <strong>\u201eThreads\u201d<\/strong>. Po jego klikni\u0119ciu ukaza\u0142 si\u0119 taki widok:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"497\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b3.jpg\" alt=\"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci\" class=\"wp-image-32503\" title=\"\" srcset=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b3.jpg 1200w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b3-300x124.jpg 300w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b3-768x318.jpg 768w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b3-495x205.jpg 495w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>W tym widoku u g\u00f3ry znajduje si\u0119 pasek, kt\u00f3ry znamy ju\u017c z widoku <strong>\u201eUtilization\u201d,<\/strong> a kt\u00f3ry tutaj pe\u0142ni funkcj\u0119 osi czasu. Za pomoc\u0105 czerwonej ramki ograniczonej po bokach uchwytami (czerwonymi kwadratami) zaznaczony jest obszar, dla kt\u00f3rego w dolnej cz\u0119\u015bci przedstawiono dane. Przesuwaj\u0105c uchwyty, mo\u017cna ograniczy\u0107 ten zakres do okre\u015blonego odcinka czasu i tym samym zobaczy\u0107 bardziej szczeg\u00f3\u0142owe dane.<\/p>\n\n\n\n<p>Poni\u017cej paska osi czasu znajduje si\u0119 wykres obrazuj\u0105cy stan w\u0105tk\u00f3w procesu (reprezentowanych na osi pionowej) w kolejnych momentach dzia\u0142ania procesu (o\u015b pozioma). W dolnej cz\u0119\u015bci widoku znajduje si\u0119 legenda przedstawiaj\u0105ca znaczenie u\u017cytych kolor\u00f3w, np. zielonym oznaczono stan dzia\u0142ania (executing) w\u0105tku, a czerwonym stan synchronizacji. Na legendzie widniej\u0105 te\u017c okre\u015blone w procentach udzia\u0142y tych stan\u00f3w w zaznaczonym obr\u0119bie czasu.<\/p>\n\n\n\n<p>W przypadku uruchomienia eksperymentalnej metody, mo\u017cna zauwa\u017cy\u0107, \u017ce proces dzia\u0142a w wi\u0119cej, ni\u017c 1 w\u0105tku, oraz \u017ce 58% czasu zosta\u0142o po\u015bwi\u0119cone na synchronizacj\u0119 w\u0105tk\u00f3w, czyli operacj\u0119 kopiowania danych z obszar\u00f3w pami\u0119ci zarezerwowanych dla poszczeg\u00f3lnych w\u0105tk\u00f3w.<strong> Po zako\u0144czeniu pracy poszczeg\u00f3lnych roboczych w\u0105tk\u00f3w nast\u0119puje zebranie wynik\u00f3w (ewentualne dodatkowe operacje na tych wynikach). Proces ten nazywamy synchronizacj\u0105 w\u0105tk\u00f3w.<\/strong> Nast\u0119puje ona po zako\u0144czeniu pracy wszystkich w\u0105tk\u00f3w, aby zapewni\u0107, \u017ce operacje wykonane w jednym w\u0105tku nie wp\u0142yn\u0105 w spos\u00f3b niekontrolowany na dane przydzielone do innych w\u0105tk\u00f3w.<\/p>\n\n\n\n<p>Pomimo \u017ce zastosowany spos\u00f3b wyliczania liczb pierwszych by\u0142 sekwencyjny, proces posiada\u0142 wiele w\u0105tk\u00f3w. Jest to cecha ka\u017cdego procesu uruchamianego w systemie Windows \u2013 po za\u0142adowaniu aplikacji (co na wykresie jest widoczne w postaci fioletowego paska w lewej cz\u0119\u015bci wykresu, oznaczonego w legendzie jako proces I\/O) tworzonych jest kilka w\u0105tk\u00f3w roboczych. Wi\u0119cej informacji na ten temat mo\u017cna znale\u017a\u0107 w ksi\u0105\u017cce \u201eWindows Internals. Seventh Edition. Part 1\u201d (Pavel Yosifovich et al.) oraz \u2013 odno\u015bnie platformy .NET \u2013 w rozdziale <em>Threads<\/em> dokumentacji <a href=\"https:\/\/github.com\/dotnet\/coreclr\/blob\/master\/Documentation\/botr\/threading.md#special-threads\" target=\"_blank\" rel=\"noopener\">The Book of the Runtime<\/a>.<\/p>\n\n\n\n<p>W kolejnym kroku uruchomi\u0142em analiz\u0119 dla wersji r\u00f3wnoleg\u0142ej. Zmieni\u0142em wi\u0119c odpowiednio metod\u0119 Main:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_9.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27422\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>i ponownie uruchomi\u0142em <strong>narz\u0119dzie Concurrency Visualizer.<\/strong> Po prze\u0142\u0105czeniu si\u0119 na zak\u0142adk\u0119 Threads m\u00f3j wykres wygl\u0105da\u0142 w ten spos\u00f3b:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"497\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b4.jpg\" alt=\"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci\" class=\"wp-image-32500\" title=\"\" srcset=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b4.jpg 1200w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b4-300x124.jpg 300w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b4-768x318.jpg 768w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b4-495x205.jpg 495w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Z wykresu wynika, \u017ce w tym przypadku <strong>synchronizacja w\u0105tk\u00f3w zaj\u0119\u0142a 63% czasu<\/strong>.<\/p>\n\n\n\n<p>R\u00f3\u017cnica pomi\u0119dzy 58% a 63% nie wydaje si\u0119 by\u0107 wielka, ale \u2013 jak wida\u0107 po wykresie z przypadku dzia\u0142ania sekwencyjnego \u2013 synchronizacja odbywa si\u0119 nie tylko wtedy, gdy wykonywane s\u0105 obliczenia wielow\u0105tkowe, ale synchronizacja jest jednym z podstawowych dzia\u0142a\u0144, kt\u00f3re odbywaj\u0105 si\u0119 w procesach. Poza tym powy\u017csze udzia\u0142y s\u0105 obliczone wzgl\u0119dem czasu trwania ca\u0142ego procesu, kt\u00f3ry obejmuje te\u017c czas zwi\u0105zany z \u0142adowaniem i uruchamianiem aplikacji, zanim aplikacja rozpocznie uruchamianie kodu, kt\u00f3ry zaimplementowali\u015bmy (dokona oblicze\u0144 czasu liczb pierwszych). Narz\u0119dzie BenchmarkDotNet uwzgl\u0119dnia te czynniki podczas mierzenia wydajno\u015bci.<\/p>\n\n\n\n<p>Dzia\u0142anie i synchronizacj\u0119 w\u0105tk\u00f3w utworzonych przez <strong>Parallel.Range()<\/strong> mo\u017cna zaobserwowa\u0107 na podstawie zakresu dzia\u0142ania oznaczonego ciemnofioletowym paskiem oznaczonym napisem <strong>\u201eParallelQueryBegin\u201d.<\/strong> Aby zaobserwowa\u0107 dzia\u0142ania przeprowadzane na poszczeg\u00f3lnych w\u0105tkach i synchronizacj\u0119 pomi\u0119dzy nimi, na osi czasu (na wykresie na samej g\u00f3rze okna) zaw\u0119zi\u0142em zakres wyznaczony przez ten pasek do zakresu oznaczonego ciemnofioletowym paskiem. Na ekranie zobaczy\u0142em wtedy taki widok:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"497\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b5.jpg\" alt=\"Pu\u0142apki wsp\u00f3\u0142bie\u017cno\u015bci\" class=\"wp-image-32497\" title=\"\" srcset=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b5.jpg 1200w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b5-300x124.jpg 300w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b5-768x318.jpg 768w, https:\/\/nearshore-it.eu\/wp-content\/uploads\/2020\/04\/jpro_grafika_2020.29.04_b5-495x205.jpg 495w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Kiedy-zrownoleglac-obliczenia\">Kiedy zr\u00f3wnolegla\u0107 obliczenia?<\/h2>\n\n\n\n<p>Kiedy w takim razie warto zr\u00f3wnolegla\u0107 obliczenia? We wspomnianym artykule <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/parallel-programming\/understanding-speedup-in-plinq\" target=\"_blank\" rel=\"noopener\">Understanding Speedup in PLINQ<\/a> Microsoft wyja\u015bnia, \u017ce musi by\u0107 spe\u0142niony warunek: <strong>koszt operacji obliczeniowych powinien przewy\u017csza\u0107 narzut czasu zwi\u0105zany z synchronizacj\u0105 w\u0105tk\u00f3w zwi\u0105zany z tymi obliczeniami.<\/strong><\/p>\n\n\n\n<p>Obliczanie liczby pierwszej, wed\u0142ug wykorzystanego w tym przyk\u0142adzie algorytmu, b\u0119dzie przebiega\u0142o szybciej dla ma\u0142ych liczb ni\u017c dla liczb wi\u0119kszych. Na kolejnym etapie test\u00f3w wr\u00f3ci\u0142em do przeprowadzania benchmark\u00f3w i sprawdzania \u015bredniego czasu wykonywania oblicze\u0144 dla zakres\u00f3w: od 1 do 100, od 1 do 10\u2019000 i od 1 do 1\u2019000\u2019000.<\/p>\n\n\n\n<p>BenchmarkDotNet <a href=\"https:\/\/benchmarkdotnet.org\/articles\/features\/parameterization.html\" target=\"_blank\" rel=\"noopener\">wspomaga parametryzacj\u0119<\/a> przeprowadzanych test\u00f3w za pomoc\u0105 atrybutu <strong>ParamsAttribute<\/strong> z przestrzeni nazw <strong>BenchmarkDotNet.Attributes.<\/strong> Ustawia si\u0119 go na publicznej w\u0142a\u015bciwo\u015bci z publicznym setterem, a opatrzonej nim w\u0142a\u015bciwo\u015bci u\u017cywa si\u0119 w testowanej metodzie:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_10.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27423\" title=\"\"><\/figure>\n<\/div>\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Po uruchomieniu test\u00f3w do tabeli wynik\u00f3w zosta\u0142a dodana kolumna z warto\u015bci\u0105 wykorzystanego parametru:<\/p>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nearshore-it.eu\/wp-content\/uploads\/2024\/09\/jpro_grafika_2020.29.04_11.png\" alt=\"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w aplikacji\" class=\"wp-image-27424\" title=\"\"><\/figure>\n\n\n\n<div style=\"height:34px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Jak wida\u0107 na wynikach: im wi\u0119kszy zakres p\u0119tli, tym bardziej korzystne okazuje si\u0119 rozwi\u0105zanie wielow\u0105tkowe.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Podsumowanie-warto-robic-pomiary\">Podsumowanie: warto robi\u0107 pomiary<\/h2>\n\n\n\n<p>Temat wprowadzania wsp\u00f3\u0142bie\u017cno\u015bci do aplikacji nie jest tematem trywialnym. W zrozumieniu tematu pomaga nie tylko znajomo\u015b\u0107 dokumentacji, ale te\u017c mechanizm\u00f3w systemowych dzia\u0142aj\u0105cych na niskim poziomie \u2013 systemu lub nawet pami\u0119ci i procesora. Dlatego, aby upewni\u0107 si\u0119, \u017ce wprowadzenie wielow\u0105tkowo\u015bci do aplikacji \u0142\u0105czy si\u0119 z realn\u0105 korzy\u015bci\u0105, warto przeprowadza\u0107 testy wydajno\u015bci.<\/p>\n\n\n\n<p><strong>Przeczytaj tak\u017ce: <a href=\"https:\/\/nearshore-it.eu\/pl\/artykuly\/test-driven-development-na-co-dzien\">Test-Driven Development na co dzie\u0144<\/a><\/strong><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>W powszechnym mniemaniu programowanie wsp\u00f3\u0142bie\u017cne zapewnia przyspieszenie dzia\u0142ania ka\u017cdej aplikacji. Entuzja\u015bci nowinek technologicznych, kt\u00f3rzy nie wg\u0142\u0119biaj\u0105 si\u0119 w szczeg\u00f3\u0142y techniczne wprowadzanych innowacji, ch\u0119tnie pokusz\u0105 si\u0119 te\u017c o wykorzystanie biblioteki TPL w swoim kodzie. Ci, kt\u00f3rzy zmierz\u0105 wyniki swoich \u201eoptymalizacji\u201d, ze zdziwieniem zauwa\u017c\u0105, \u017ce nie zawsze i nie ka\u017cde u\u017cycie takich rozwi\u0105za\u0144 skutkuje przyspieszeniem dzia\u0142ania kodu, a w wielu przypadkach mo\u017ce spowodowa\u0107 wr\u0119cz jego spowolnienie. Sk\u0105d wynika taka sytuacja?<\/p>\n","protected":false},"author":175,"featured_media":31287,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"iawp_total_views":47,"footnotes":""},"categories":[1,582],"tags":[574],"offering":[522],"class_list":["post-31252","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artykuly","category-technologie","tag-net-pl","offering-tech-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/31252","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\/175"}],"replies":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/comments?post=31252"}],"version-history":[{"count":4,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/31252\/revisions"}],"predecessor-version":[{"id":33956,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/posts\/31252\/revisions\/33956"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media\/31287"}],"wp:attachment":[{"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/media?parent=31252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/categories?post=31252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/tags?post=31252"},{"taxonomy":"offering","embeddable":true,"href":"https:\/\/nearshore-it.eu\/pl\/wp-json\/wp\/v2\/offering?post=31252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}