{"id":25134,"date":"2023-10-24T05:00:00","date_gmt":"2023-10-24T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=25134"},"modified":"2023-10-24T10:45:27","modified_gmt":"2023-10-24T08:45:27","slug":"porownanie-wydajnosci-mechanizmu-qt-signals-i-slots-oraz-natywnej-implementacji-c","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/porownanie-wydajnosci-mechanizmu-qt-signals-i-slots-oraz-natywnej-implementacji-c\/","title":{"rendered":"Por\u00f3wnanie wydajno\u015bci mechanizmu Qt Signals i Slots oraz natywnej implementacji C++"},"content":{"rendered":"\n<p>J\u0119zyk C++ jest cz\u0119sto wybierany, gdy wydajno\u015b\u0107 projektowanego systemu ma kluczowe znaczenie. Niestety samo jego u\u017cycie nie zagwarantuje, \u017ce osi\u0105gni\u0119ta zostanie za\u0142o\u017cona szybko\u015b\u0107 przetwarzania. Dlatego coraz wi\u0119kszy nacisk k\u0142adzie si\u0119 na optymalizacj\u0119 kodu \u017ar\u00f3d\u0142owego i na czas jego wykonywania.<a> <\/a>Jednym z aspekt\u00f3w, kt\u00f3ry ma wp\u0142yw na wydajno\u015b\u0107, jest ilo\u015b\u0107 kopiowanych danych.<\/p>\n\n\n\n<p>W niniejszym artykule skupi\u0119 si\u0119 na liczbie kopii obiekt\u00f3w wykonywanych przez bardzo popularn\u0105 bibliotek\u0119 <em>Qt <\/em>w wersji <em>6.2.4<\/em>, a dok\u0142adnie na jej mechanizmie zwanym <em>\u201eSignal &amp; Slot\u201d<\/em>. Przedstawi\u0119 r\u00f3wnie\u017c por\u00f3wnanie z w\u0142asn\u0105 implementacj\u0105 stworzonej przeze mnie biblioteki <em>TBC<\/em> (Template Based Communication).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Biblioteka QT<\/strong><\/h2>\n\n\n\n<p>Mechanizm <em>\u201eSignal &amp; Slot\u201d<\/em> w <em>Qt<\/em> s\u0142u\u017cy do synchronicznej lub asynchronicznej komunikacji mi\u0119dzy obiektami. Aby m\u00f3c z niego korzysta\u0107, obiekty musz\u0105 dziedziczy\u0107 po klasie <em>QObject<\/em> oraz u\u017cy\u0107 makro \u201eQ_OBJECT\u201d w swojej deklaracji. Obiekt wysy\u0142aj\u0105cy wiadomo\u015b\u0107 deklaruje sygna\u0142 wraz z argumentami, kt\u00f3re musz\u0105 by\u0107 kopiowalne:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass Sender : public QObject {\n\tQ_OBJECT\n\nsignals:\n    void customSignal(int value);\n};\n<\/pre><\/div>\n\n\n<p>&nbsp;Obiekt odbieraj\u0105cy deklaruje odpowiadaj\u0105cy s<em>lot<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass Receiver : public QObject {\n\tQ_OBJECT\n\npublic slots:\n\tvoid customSlot(int value);\n\n};\n<\/pre><\/div>\n\n\n<p>Nast\u0119pnie, obiekty nale\u017cy po\u0142\u0105czy\u0107 przez wykonanie metody statycznej <em>QObject::connect<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nQObject::connect(&amp;senderObject,\n                 &amp;Sender::customSignal,\n\t\t\t\t &amp;receiverObject,\n\t\t\t\t &amp;Receiver::customSlot);\n<\/pre><\/div>\n\n\n<p>Metoda ta przyjmuje r\u00f3wnie\u017c opcjonalny parametr, kt\u00f3ry definiuje typ po\u0142\u0105czenia. Skupi\u0119 si\u0119 na dw\u00f3ch najwa\u017cniejszych:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>Qt::DirectConnection<\/em> \u2013 po\u0142\u0105czenie bezpo\u015brednie. Metoda zdefiniowana jako slot zostanie wykonana synchronicznie w tym samym w\u0105tku, tak jakby zosta\u0142a bezpo\u015brednio wywo\u0142ana jako slot.<\/li>\n\n\n\n<li><em>Qt::QueuedConnection<\/em> \u2013 po\u0142\u0105czenie kolejkuj\u0105ce. Metoda zdefiniowana jako slot zostanie wykonana asynchronicznie:<ul><li>W tym samym w\u0105tku, gdy wr\u00f3ci do g\u0142\u00f3wnej p\u0119tli <em>Qt<\/em> i to wywo\u0142anie b\u0119dzie nast\u0119pne w kolejce.<\/li><\/ul>\n<ul class=\"wp-block-list\">\n<li>W innym w\u0105tku, gdy zostanie wcze\u015bniej wykonana metoda <em>QObject::moveToThread<\/em> na obiekcie odbieraj\u0105cym.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Szczeg\u00f3\u0142y tego mechanizmu s\u0105 <a aria-label=\"zawarte w dokumentacji Qt (opens in a new tab)\" href=\"https:\/\/doc.qt.io\/qt-6\/signalsandslots.html\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >zawarte w dokumentacji <em>Qt<\/em><\/a>. Powy\u017cej opisana funkcjonalno\u015b\u0107 wydaje si\u0119 bardzo u\u017cyteczna i \u0142atwa w u\u017cyciu.<\/p>\n\n\n\n<p>W dalszej cz\u0119\u015bci artyku\u0142u skupi\u0119 si\u0119 na sprawdzeniu liczby tworzonych kopii tylko w po\u0142\u0105czeniu kolejkuj\u0105cym. Napisa\u0142em prosty test przesy\u0142aj\u0105cy obiekt, kt\u00f3ry zlicza kopie poprzez inkrementacj\u0119 zmiennej statycznej w konstruktorze kopiuj\u0105cym. Poni\u017cej znajduje si\u0119 definicja tej klasy:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace\n{\n    static int copyCounter = 0;\n}\n\nclass Msg {\npublic:\n    Msg() = default;\n\n    Msg(const Msg&amp;) {\n        ++::copyCounter;\n    }\n\n    Msg(Msg&amp;&amp;) = default;\n\n    int copyCounter() const {\n        return ::copyCounter;\n    }\n};\n<\/pre><\/div>\n\n\n<p>Po dostarczeniu parametru do slotu, liczba kopii zostaje wypisana na konsol\u0119. Wyniki tego testu s\u0105 przedstawione w poni\u017cszej tabeli:<\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter\"><table><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Typ parametru w sygnale<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>Typ argumentu w slocie<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>Liczba wykonanych kopii<\/strong><\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">Msg<\/td><td class=\"has-text-align-center\" data-align=\"center\">2<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Msg&amp;&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Msg&amp;&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">Msg<\/td><td class=\"has-text-align-center\" data-align=\"center\">2<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Tab. 1 Wynik testu przesy\u0142aj\u0105cego obiekt<\/figcaption><\/figure>\n\n\n\n<p>Jak mo\u017cna zaobserwowa\u0107, w przypadku po\u0142\u0105czenia kolejkuj\u0105cego liczba wykonanych kopii jest zale\u017cna od typu parametru w sygnale i argumentu w slocie. Nale\u017cy zwr\u00f3ci\u0107 uwag\u0119, \u017ce w dw\u00f3ch ostatnich przypadkach parametr podawany jest przez <em>rvalue<\/em>. Obci\u0105\u017cenie wydajno\u015bciowe wynikaj\u0105ce z tych kopii przedstawi\u0119 w osobnym rozdziale.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Biblioteka TBC<\/strong><\/h2>\n\n\n\n<p>Dokumentacja <em>Qt<\/em> wspomina, \u017ce biblioteka musi wykonywa\u0107 kopie obiekt\u00f3w, aby przechowywa\u0107 je \u201eza kulisami\u201d, ale nigdzie nie definiuje, ile kopii jest wykonanych dla r\u00f3\u017cnych typ\u00f3w argument\u00f3w. Moim celem jest udowodnienie, \u017ce liczb\u0119 kopii dla po\u0142\u0105czenia kolejkuj\u0105cego mo\u017cna zminimalizowa\u0107, wykorzystuj\u0105c semantyk\u0119 przenoszenia lub poprzez zagwarantowanie przez u\u017cytkownika, \u017ce referencja obiektu b\u0119dzie wa\u017cna w momencie wywo\u0142ania slotu. W tym celu napisa\u0142em swoj\u0105 w\u0142asn\u0105 bibliotek\u0119 <em>TBC<\/em> (Template Based Communication).<\/p>\n\n\n\n<p>Aby unikn\u0105\u0107 konieczno\u015bci tworzenia w\u0142asnego metaj\u0119zyka, skorzysta\u0142em z mechanizmu szablon\u00f3w, dzi\u0119ki kt\u00f3remu mog\u0119 zdefiniowa\u0107 jakie argumenty b\u0119d\u0105 przesy\u0142ane. Obiekt wysy\u0142aj\u0105cy musi dziedziczy\u0107 po klasie <em>TBC::Sender&lt;T&gt;<\/em>, natomiast obiekt odbieraj\u0105cy po klasie <em>TBC::Receiver&lt;T&gt;<\/em>, gdzie \u201eT\u201d jest typem wysy\u0142anego argumentu. Sygna\u0142 wysy\u0142any jest na dwa sposoby:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>valueSignal(T )<\/em> \u2013 sygna\u0142 przyjmuje argument przez warto\u015b\u0107, co pozwala na zastosowanie semantyki przenoszenia.<\/li>\n\n\n\n<li><em>constRefSignal(const T&amp; )<\/em> \u2013 sygna\u0142 przyjmuje sta\u0142\u0105 referencje do obiektu. Obiekt zostanie skopiowany jedynie wtedy, gdy slot przyjmuje argument jako warto\u015b\u0107. Wysy\u0142any obiekt nie mo\u017ce zosta\u0107 zniszczony przed wywo\u0142aniem po\u0142\u0105czonego slotu.<\/li>\n<\/ul>\n\n\n\n<p>Analogicznie obs\u0142ugiwany jest odbi\u00f3r argument\u00f3w poprzez <em>valueSlot(T )<\/em> i <em>constRefSlot(const T&amp; ).<\/em> Przyk\u0142ad u\u017cycia w kodzie jest zaprezentowany poni\u017cej:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass Sender : public TBC::Sender&lt;LargeObj&gt; {\npublic:\n    void sendValue(LargeObj value) {\n        valueSignal(std::move(value));\n    }\n\n    void sendconstRef(const LargeObj&amp; ref) {\n        constRefSignal(ref);\n    }\n};\n\nclass Receiver : public TBC::Receiver&lt;LargeObj&gt; {\npublic:\n    void valueSlot(LargeObj value) override {}\n\t\n\tvoid constRefSlot(const LargeObj&amp; ref) override {}\n};\n<\/pre><\/div>\n\n\n<p>Aby po\u0142\u0105czy\u0107 obiekty mo\u017cna u\u017cy\u0107 statycznej metody <em>TBC::connect<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nTBC::connect(&amp;sender, &amp;receiver);\n<\/pre><\/div>\n\n\n<p>Po szczeg\u00f3\u0142y odsy\u0142am do <a href=\"https:\/\/github.com\/ksierocinski\/TBC\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >mojego repozytorium<\/a>, gdzie mo\u017cna znale\u017a\u0107 implementacj\u0119 biblioteki <em>TBC<\/em>, diagram klas oraz testy funkcjonalne i wydajno\u015bciowe. Wszystkie komentarze i uwagi mile widziane \ud83d\ude42<\/p>\n\n\n\n<p>Tak jak w przypadku <em>Qt,<\/em> przeprowadzi\u0142em analogiczne testy w celu zbadania liczby wykonanych kopii dla po\u0142\u0105czenia kolejkuj\u0105cego. Wyniki test\u00f3w znajduj\u0105 si\u0119 w poni\u017cszej tabeli:<\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter\"><table><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Typ parametru w sygnale<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>Typ argumentu w slocie<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>Liczba kopii<\/strong><\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">Msg<\/td><td class=\"has-text-align-center\" data-align=\"center\">1<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Msg&amp;&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">const Msg&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">0<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Msg&amp;&amp;<\/td><td class=\"has-text-align-center\" data-align=\"center\">Msg<\/td><td class=\"has-text-align-center\" data-align=\"center\">0<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Tab. 2 Wyniki test\u00f3w dot. liczby wykonanych kopii<\/figcaption><\/figure>\n\n\n\n<p>Mo\u017cna zauwa\u017cy\u0107, \u017ce biblioteka TBC zapewnia minimaln\u0105 liczb\u0119 wymaganych kopii dla po\u0142\u0105czenia kolejkuj\u0105cego.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Testy wydajno\u015bciowe<\/strong><\/h2>\n\n\n\n<p>Przeprowadzone testy wydajno\u015bciowe mierzy\u0142y czas, jaki up\u0142yn\u0105\u0142 od emisji sygna\u0142u do wywo\u0142ania slotu, kt\u00f3ry dzia\u0142a w osobnym w\u0105tku, dla r\u00f3\u017cnych rozmiar\u00f3w przesy\u0142anego parametru. Ka\u017cdy test wydajno\u015bciowy wykonywa\u0142 50 iteracji. Testy zosta\u0142y uruchomione dwukrotnie, daj\u0105c \u0142\u0105cznie 100 pomiar\u00f3w, z kt\u00f3rych obliczono \u015bredni czas operacji.<\/p>\n\n\n\n<p>Konfiguracja systemu, na kt\u00f3rym przeprowadzono testy, by\u0142a nast\u0119puj\u0105ca:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>System operacyjny: Ubuntu 22.04.2 LTS<\/li>\n\n\n\n<li>Kompilator: gcc 11.3.0<\/li>\n\n\n\n<li>Wersja biblioteki Qt: 6.2.4<\/li>\n<\/ul>\n\n\n\n<p>Pierwszy test wydajno\u015bciowy mierzy\u0142 przes\u0142anie obiektu typu <em>std::chrono::high_resolution_clock::time_point<\/em> ustawionego zaraz przed emisj\u0105 sygna\u0142u oraz analizowa\u0142 op\u00f3\u017anienia w slocie odbiorcy. Dla zobrazowania, fragment kodu korzystaj\u0105cego z Qt:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nQObject::connect(&amp;sender, &amp;QTSender::send, &amp;receiver, &amp;QTReceiver::valueSlot, Qt::QueuedConnection);\nnewThread.start();\n\nstd::cout &lt;&lt; &quot;QT latency &#x5B;\u00b5s]: &quot;;\nfor (size_t i = 0; i &lt; iterations; ++i) {\n\tsender.send(std::chrono::high_resolution_clock::now());\n\tstd::this_thread::sleep_for(std::chrono::seconds{1});\n}\nstd::cout &lt;&lt; std::endl;\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\npublic slots:\n    void valueSlot(std::chrono::high_resolution_clock::time_point sendTimePoint) {\n        auto endTimePoint = std::chrono::high_resolution_clock::now();\n        std::cout &lt;&lt; std::chrono::duration_cast&lt;std::chrono::microseconds&gt; (endTimePoint - sendTimePoint).count() &lt;&lt; &quot;,&quot;;\n    }\n<\/pre><\/div>\n\n\n<p>oraz TBC:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nTBC::connect(&amp;sender, &amp;receiver);\nreceiver.runInNewThread();\n\nstd::cout &lt;&lt; &quot;TBC latency &#x5B;\u00b5s]: &quot;;\nfor (size_t i = 0; i &lt; iterations; ++i) {\n\tsender.valueSignal(std::chrono::high_resolution_clock::now());\n\tstd::this_thread::sleep_for(std::chrono::seconds{1});\n}\nstd::cout &lt;&lt; std::endl;\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\npublic:\n    void valueSlot(std::chrono::high_resolution_clock::time_point sendTimePoint) override {\n        auto endTimePoint = std::chrono::high_resolution_clock::now();\n        std::cout &lt;&lt; std::chrono::duration_cast&lt;std::chrono::microseconds&gt; (endTimePoint - sendTimePoint).count() &lt;&lt; &quot;,&quot;;\n    }\n<\/pre><\/div>\n\n\n<p>Wyniki testu s\u0105 zobrazowane poni\u017cej:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img decoding=\"async\" width=\"1024\" height=\"560\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-1024x560.png\" alt=\"\u015arednie op\u00f3\u017anienie dostarczania wiadomo\u015bci\" class=\"wp-image-25146\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-1024x560.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-300x164.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-768x420.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-1536x841.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Latency-2048x1121.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Ryc. 1 \u015arednie op\u00f3\u017anienie dostarczania wiadomo\u015bci <\/figcaption><\/figure>\n\n\n\n<p>Chocia\u017c biblioteka <em>TBC<\/em> wykazuje oko\u0142o 10% mniejsze op\u00f3\u017anienie wzgl\u0119dem biblioteki <em>Qt<\/em>, to nie jest to znacz\u0105ca r\u00f3\u017cnica, poniewa\u017c dopiero po 100 tysi\u0105cach operacji op\u00f3\u017anienie zsumuje si\u0119 do 1 sekundy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Dalsze testy wydajno\u015bciowe<\/strong><\/h2>\n\n\n\n<p>Kolejne testy wydajno\u015bciowe mierzy\u0142y czas potrzebny na przes\u0142anie parametru <em>Msg<\/em>, kt\u00f3ry zawiera tablic\u0119 bajt\u00f3w o zmiennej d\u0142ugo\u015bci, przy czym rozmiar tej tablicy by\u0142 mno\u017cony czterokrotnie, zaczynaj\u0105c od 1kB, a\u017c do 256MB. Kod \u017ar\u00f3d\u0142owy klasy Msg zaprezentowano poni\u017cej:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass Msg {\n    std::vector&lt;uint8_t&gt; _data;\n    std::chrono::high_resolution_clock::time_point _msgCreationTimePoint;\n\npublic:\n    Msg() = default;\n\n    Msg(size_t msgByteSizeInKb) :\n        _data(msgByteSizeInKb * 1024),\n        _msgCreationTimePoint{std::chrono::high_resolution_clock::now()}\n    {}\n\n    Msg(const Msg&amp; other) = default;\n\n    Msg(Msg&amp;&amp; other) = default;\n\n    void resetCreationTimePoint() {\n        _msgCreationTimePoint = std::chrono::high_resolution_clock::now();\n    }\n\n    const std::chrono::high_resolution_clock::time_point&amp; sendTimePoint() const {\n        return _msgCreationTimePoint;\n    }\n\n    size_t dataSizeInKb() const {\n        return _data.size() \/ 1024;\n    }\n\n    static constexpr int maxMsgSizeInKb = power(8, 6);\n};\n<\/pre><\/div>\n\n\n<p>Testy wykonano dla wszystkich kombinacji typ\u00f3w parametru sygna\u0142u i argumentu slotu, kt\u00f3re zosta\u0142y przedstawione w tabelach w poprzednich sekcjach. Poni\u017cej znajduj\u0105 si\u0119 wykresy stworzone na podstawie osi\u0105gni\u0119tych wynik\u00f3w:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined.png\"><img decoding=\"async\" width=\"1024\" height=\"652\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-1024x652.png\" alt=\"Ryc. 13 Wyniki test\u00f3w\" class=\"wp-image-25150\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-1024x652.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-300x191.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-768x489.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-1536x977.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Benchmark_combined-2048x1303.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 2 Wyniki test\u00f3w<\/figcaption><\/figure>\n\n\n\n<p>Wykresy pokazuj\u0105, \u017ce czas wykonania operacji przez <em>Qt<\/em> w ka\u017cdym przypadku jest liniowo zale\u017cny od rozmiaru parametru, a czasy koreluj\u0105 z wcze\u015bniej deklarowanymi warto\u015bciami liczby wykonywanych kopii. W przypadku <em>TBC, <\/em>dla trzech sytuacji<em>,<\/em> w kt\u00f3rych mo\u017cna unikn\u0105\u0107 kopii obiektu, wykazuje on sta\u0142\u0105 z\u0142o\u017cono\u015b\u0107, co oznacza, \u017ce czas przes\u0142ania danych jest niezale\u017cny od ich wielko\u015bci.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Testy wydajno\u015bciowe potwierdzi\u0142y, \u017ce <strong>biblioteka <em>Qt<\/em> (w wersji 6.2.4) nieoptymalnie zarz\u0105dza pami\u0119ci\u0105 w przeprowadzonych testach<\/strong>. Niestety, w dokumentacji <em>Qt <\/em>nie ma jasnej wzmianki o takim ograniczeniu wydajno\u015bciowym. Stworzona przeze mnie biblioteka <em>TBC<\/em> udowadnia, \u017ce mo\u017cna osi\u0105gn\u0105\u0107 optymaln\u0105 liczb\u0119 operacji kopiowania obiekt\u00f3w, zachowuj\u0105c analogiczny i przyjazny interfejs. Mo\u017cliwe, \u017ce w kolejnych wersjach <em>Qt<\/em> otrzymamy aktualizacje dodaj\u0105c\u0105 wparcie operacji przenoszenia parametr\u00f3w.<\/p>\n\n\n\n<p>Tymczasem, u\u017cywaj\u0105c mechanizmu \u201eSignal &amp; Slot\u201d z Qt:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Nale\u017cy pami\u0119ta\u0107, \u017ce obiekt musi by\u0107 kopiowalny i zostanie on skopiowany przynajmniej raz przy emisji sygna\u0142u w trybie kolejkuj\u0105cym.<\/li>\n\n\n\n<li>Preferowane jest przyjmowanie parametru w slocie jako sta\u0142ej referencj\u0119, dzi\u0119ki czemu uniknie si\u0119 wykonania jednej kopii.<\/li>\n\n\n\n<li>Je\u015bli jest taka mo\u017cliwo\u015b\u0107, warto zapakowa\u0107 parametr w <em>std::shared_ptr<\/em>, aby unikn\u0105\u0107 kopiowania obiektu.<\/li>\n<\/ul>\n\n\n\n<p>Przy korzystaniu ze <em>std::shared_ptr<\/em>, nale\u017cy pami\u0119ta\u0107, \u017ce parametr wewn\u0105trz zostanie zniszczony w momencie usuni\u0119cia ostatniej kopii <em>std::shared_ptr<\/em>, chyba \u017ce ustawiona zostanie inna funkcja destrukcji obiektu, tzw. \u201ecustom deleter\u201d. Najbezpieczniej jest u\u017cy\u0107 metody <em>std::make_shared<\/em> na etapie alokacji pami\u0119ci, aby nie wywo\u0142a\u0107 destruktora przechowywanego obiektu i nie zwolni\u0107 jego pami\u0119ci drugi raz.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Bibliografia<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/embeddeduse.com\/2013\/06\/29\/copied-or-not-copied-arguments-signals-slots\/\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >Burkhard Stubert&nbsp; \u201cCopied or Not Copied: Arguments in Signal-Slot Connections?\u201d<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/doc.qt.io\/qt-5\/signalsandslots.html\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >Signals &amp; Slots<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ksierocinski\/TBC\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >Repozytorium \u2013 Karol Sieroci\u0144ski<\/a><\/li>\n<\/ul>\n\n\n\n<p>***<\/p>\n\n\n\n<p>Je\u015bli interesuje Ci\u0119 tematyka C++, zajrzyj r\u00f3wnie\u017c do <a href=\"https:\/\/sii.pl\/blog\/wyszukiwarka\/c%2B%2B\/\" target=\"_blank\" aria-label=\"innych artyku\u0142\u00f3w naszych ekspert\u00f3w (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">innych artyku\u0142\u00f3w naszych ekspert\u00f3w<\/a>. <\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;25134&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;5&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 5)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Por\u00f3wnanie wydajno\u015bci mechanizmu Qt Signals i Slots oraz natywnej implementacji C++&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 5)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>J\u0119zyk C++ jest cz\u0119sto wybierany, gdy wydajno\u015b\u0107 projektowanego systemu ma kluczowe znaczenie. Niestety samo jego u\u017cycie nie zagwarantuje, \u017ce osi\u0105gni\u0119ta &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/porownanie-wydajnosci-mechanizmu-qt-signals-i-slots-oraz-natywnej-implementacji-c\/\">Continued<\/a><\/p>\n","protected":false},"author":577,"featured_media":25155,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[563,362,1058],"class_list":["post-25134","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-embedded","tag-biblioteka","tag-cpp"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/10\/Porownanie-wydajnosci-mechanizmu-Qt-Signals-i-Slots-oraz-natywnej-implementacji-C.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/25134"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/577"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=25134"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/25134\/revisions"}],"predecessor-version":[{"id":25190,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/25134\/revisions\/25190"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/25155"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=25134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=25134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=25134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}