signal/slot - вопрос по быстродействию
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.10 10:08
Оценка:
Здравствуте, коллеги!

Меня интересует, как сигналы связываются со слотами. Как я понимаю, есть некое хранилище даных связей, единое для всего приложения, типа map/multimap, в котором при возникновении сигнала ищутся слоты, которые к нему присоединены. У меня возникает вопрос — если приложение большое, содержит много GUI, использует множество сигналов, то по идее оно должно тормозить, и чем больше будет разрастаться гуй, тем сильнее будут тормоза. Или все устроено несколько более оптимально и быстродействие сигнало не зависит от размера гуя?
Маньяк Робокряк колесит по городу
Re: signal/slot - вопрос по быстродействию
От: ocaml  
Дата: 03.11.10 10:22
Оценка:
Здравствуйте, Marty, Вы писали:

M> Здравствуте, коллеги!


M> Меня интересует, как сигналы связываются со слотами. Как я понимаю, есть некое хранилище даных связей, единое для всего приложения, типа map/multimap, в котором при возникновении сигнала ищутся слоты, которые к нему присоединены. У меня возникает вопрос — если приложение большое, содержит много GUI, использует множество сигналов, то по идее оно должно тормозить, и чем больше будет разрастаться гуй, тем сильнее будут тормоза. Или все устроено несколько более оптимально и быстродействие сигнало не зависит от размера гуя?


http://www.qtinfo.ru/signalsandslots

Цитаты:

Расширение языка C++ реализовано с помощью специального препроцессора MOC (Meta Object Compiler). Он анализирует классы на наличие макроса Q_OBJECT и создает дополнительные файлы с кодом на "чистом" C++, содержащие необходимую функциональность. Так как эта работа выполняется полностью автоматически, то вспоминать об этом механизме вам не придется. Если у вас все же возникла необходимость углубиться в детали реализации,то вы всегда можете просмотреть файлы, сгенерированные метаобъектным компилятором.


Недостатки использования механизма сигнал-слот:

* Необходимость наследования класса QObject.
* Сигналы и слоты несколько медленнее, чем вызовы функций при использовании механизма функций обратного вызова. Однако производительность механизма сигнал-слот все же весьма высока. Так, по информации Trolltech, на i586-500 за одну секунду может быть послано около 2000000 сигналов с одним получателем, либо около 1200000 с двумя.
* В процессе компиляции не ведется проверка совместимости сигнала со слотом или наличия данного сигнала или слота в данном классе. Сообщение об ошибке будет получено на консоль во время исполнения программы.


Препроцессором механизм реализуется.
avalon 1.0rc3 rev 364, zlib 1.2.3
Re[2]: signal/slot - вопрос по быстродействию
От: blackhearted Украина  
Дата: 03.11.10 10:29
Оценка:
Здравствуйте, ocaml, Вы писали:


O>

Недостатки использования механизма сигнал-слот:

O> * Необходимость наследования класса QObject.
O> * Сигналы и слоты несколько медленнее, чем вызовы функций при использовании механизма функций обратного вызова. Однако производительность механизма сигнал-слот все же весьма высока. Так, по информации Trolltech, на i586-500 за одну секунду может быть послано около 2000000 сигналов с одним получателем, либо около 1200000 с двумя.
O> * В процессе компиляции не ведется проверка совместимости сигнала со слотом или наличия данного сигнала или слота в данном классе. Сообщение об ошибке будет получено на консоль во время исполнения программы.


O>Препроцессором механизм реализуется.


Было бы интересно, если бы указывались не абсолютные цифры, а процент замедления по сравнению с callbacks(хотя бы для случая с 1 получателем).
Re[3]: signal/slot - вопрос по быстродействию
От: ocaml  
Дата: 03.11.10 10:34
Оценка:
Здравствуйте, blackhearted, Вы писали:

b> Было бы интересно, если бы указывались не абсолютные цифры, а процент замедления по сравнению с callbacks(хотя бы для случая с 1 получателем).


Я бы начал с просмотра сгенерированных исходников moc'ом (что и рекомендуется по ссылке, кстати). Думаю, это бы дало наиболее верное представление о том, что происходит внутри. Сам таким вопросом не задавался пока, быстродействием монстрообразных qt4-программ доволен
avalon 1.0rc3 rev 364, zlib 1.2.3
Re[4]: signal/slot - вопрос по быстродействию
От: blackhearted Украина  
Дата: 03.11.10 10:37
Оценка:
Здравствуйте, ocaml, Вы писали:

O>Здравствуйте, blackhearted, Вы писали:


b>> Было бы интересно, если бы указывались не абсолютные цифры, а процент замедления по сравнению с callbacks(хотя бы для случая с 1 получателем).


O>Я бы начал с просмотра сгенерированных исходников moc'ом (что и рекомендуется по ссылке, кстати). Думаю, это бы дало наиболее верное представление о том, что происходит внутри. Сам таким вопросом не задавался пока, быстродействием монстрообразных qt4-программ доволен


Я не спорю, но хотелось бы относительных цифр. Исходники почитать — это неплохо, но бенчмарков не заменит.
Re[2]: signal/slot - вопрос по быстродействию
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 03.11.10 11:16
Оценка:
Здравствуйте, ocaml, Вы писали:

Общие сведения я читал, хотелось бы узнать, хотя бы в общих чертах, как устроено связывание сигналов и слотов.

O>Препроцессором механизм реализуется.

Это в стиле магистра Йоды?
На самом деле, если бы препроцессором все ограничивалось, то в рантайме нельзя было бы связывать сигналы и слоты, а это не так. Значит, есть рантайм поддержка этого механизма.
Маньяк Робокряк колесит по городу
Re[3]: signal/slot - вопрос по быстродействию
От: ocaml  
Дата: 03.11.10 11:30
Оценка:
Здравствуйте, Marty, Вы писали:

M> O>Препроцессором механизм реализуется.


M> Это в стиле магистра Йоды?


Нет.

M> На самом деле, если бы препроцессором все ограничивалось, то в рантайме нельзя было бы связывать сигналы и слоты, а это не так. Значит, есть рантайм поддержка этого механизма.


Да.
avalon 1.0rc3 rev 364, zlib 1.2.3
Re: signal/slot - вопрос по быстродействию
От: Chorkov Россия  
Дата: 03.11.10 17:51
Оценка: 50 (6)
Здравствуйте, Marty, Вы писали:

M> Здравствуте, коллеги!


M> Меня интересует, как сигналы связываются со слотами. Как я понимаю, есть некое хранилище даных связей, единое для всего приложения, типа map/multimap, в котором при возникновении сигнала ищутся слоты, которые к нему присоединены. У меня возникает вопрос — если приложение большое, содержит много GUI, использует множество сигналов, то по идее оно должно тормозить, и чем больше будет разрастаться гуй, тем сильнее будут тормоза. Или все устроено несколько более оптимально и быстродействие сигнало не зависит от размера гуя?


Связи (сигнал<->слот) хранятся не в общей для приложения карте, а в каждом из QObject-ов (и во владельце сигнала, и во владельце слота).
В деструкторе каждого из вледельцев связи вызывается disconnect для связи.
Поиск нужного члена (при соединении) производится по строковому имени сигнала/слота. Поэтому время подключения будет рости с ростом числа сигналов/слотов в нем.
После того как соединение установлено — хранится только номер сигнала/слота. В момент вызова, функция из номера получается с помощью оператора switch, т.е. достаточно быстро.

Связь хранится в виде числа (номер сигнала/слота) + указатель на владельца. Вызов реализован в виде switch. Не думаю, что это медленнее чем хранение указателя на функцию в boost::signal. Трудозатраты в связи с упаковкой аргументов вызова в виде виде void **_a и последующей распаковкой каждого из аргументов через " *reinterpret_cast< int(*)>(_a[1]) " возможно, даже меньше чем в случае других видов сигналов (в boost::signals аргументы копируются несколько раз, пока дойдут до получателя).

Резульаты теста (msvc2005, Qt 4.4, Boost 1.41):
                                    name             clock             min             max

                      Qt connection time             290.7             265             422
             Qt void(const string&) 1->1              26.6              15              78
            Qt void(const string&) 1->10             126.6             125             141
                       Qt void(int) 1->1                28              15              32
                      Qt void(int) 1->10             126.6             125             141
                   Qt void(int,int) 1->1              20.4              15              32
                  Qt void(int,int) 1->10               125             125             125
               Qt void(int,int,int) 1->1                22              15              32
              Qt void(int,int,int) 1->10               125             125             125
                    Qt void(string) 1->1              54.7              47              93
                   Qt void(string) 1->10             213.9             203             219
                      Qt void(void) 1->1              28.2              16              32
                     Qt void(void) 1->10             126.5             125             140

           boost::signal connection time             323.3             297             344
  boost::signal void(const string&) 1->1              89.1              63             203
 boost::signal void(const string&) 1->10             274.9             265             282
            boost::signal void(int) 1->1              76.6              63              79
           boost::signal void(int) 1->10             276.5             265             282
        boost::signal void(int,int) 1->1              73.4              62              79
       boost::signal void(int,int) 1->10             285.9             281             297
    boost::signal void(int,int,int) 1->1              71.8              62              78
   boost::signal void(int,int,int) 1->10             289.2             281             297
         boost::signal void(string) 1->1             370.3             359             407
        boost::signal void(string) 1->10              2075            2047            2094
           boost::signal void(void) 1->1                75              63              78
          boost::signal void(void) 1->10               278             266             281

          boost::signal2 connection time             198.5             172             297
 boost::signal2 void(const string&) 1->1              59.4              47              63
boost::signal2 void(const string&) 1->10             207.9             203             219
           boost::signal2 void(int) 1->1              61.1              47              63
          boost::signal2 void(int) 1->10             204.6             203             219
       boost::signal2 void(int,int) 1->1              60.8              47              63
      boost::signal2 void(int,int) 1->10             212.6             203             219
   boost::signal2 void(int,int,int) 1->1              62.7              62              63
  boost::signal2 void(int,int,int) 1->10               217             203             219
        boost::signal2 void(string) 1->1             337.5             296             484
       boost::signal2 void(string) 1->10              2161            1921            3078
          boost::signal2 void(void) 1->1              60.8              47              63
         boost::signal2 void(void) 1->10             199.9             187             204

                handmake connection time              26.6              15              32
       handmake void(const string&) 1->1                 0               0               0
      handmake void(const string&) 1->10              12.4               0              16
                 handmake void(int) 1->1               4.8               0              16
                handmake void(int) 1->10               9.4               0              16
             handmake void(int,int) 1->1               1.5               0              15
            handmake void(int,int) 1->10              14.1               0              16
         handmake void(int,int,int) 1->1               1.6               0              16
        handmake void(int,int,int) 1->10                14               0              16
              handmake void(string) 1->1              81.2              78              94
             handmake void(string) 1->10             420.3             406             422
                handmake void(void) 1->1               1.5               0              15
               handmake void(void) 1->10              10.8               0              16

Где handmake — наиболее близкий аналог для случая прямого вызова. Когда указатель на вызываемую функцию известен на этапе компиляции, в сигнале запоминаются только указатели на обьект-получатель.

Исходный код теста: http://files.rsdn.ru/4220/test_signals_qt.zip

Реализация сигналов Qt вполне конкурентноспособна, в сравнениями с другими реализациями.
Но в критических (по производительности) ситуациях, когда обработчик сигнала очень мал по размеру, идеому сигналов лучше не использовать (лучшие реализации работают в 20 раз медленнее, чем просто вызов известной функции).
Re: signal/slot - вопрос по быстродействию
От: vlad-mal  
Дата: 04.11.10 01:01
Оценка: 4 (1)
Здравствуйте, Marty, Вы писали:

M> ...У меня возникает вопрос — если приложение большое, содержит много GUI, использует множество сигналов, то по идее оно должно тормозить, и чем больше будет разрастаться гуй, тем сильнее будут тормоза. Или все устроено несколько более оптимально и быстродействие сигнало не зависит от размера гуя?


Да, может и тормозить.

К счастью — не только слоты/сигналы.
В книжке, в главе 7 описан механизм событий.
Отмечено, что, как правило, сигналы полезны при использовании виджета, в то время как события полезны при реализации виджета.
Re[3]: signal/slot - вопрос по быстродействию
От: Eye of Hell Россия eyeofhell.habr.ru
Дата: 04.11.10 10:05
Оценка:

На самом деле, если бы препроцессором все ограничивалось, то в рантайме нельзя было бы связывать сигналы и слоты, а это не так. Значит, есть рантайм поддержка этого механизма.


Препроцессор генерирует код для поддержки connect() и emit в рантайме.
Re[5]: signal/slot - вопрос по быстродействию
От: Truf Россия http://meego.fruct.org
Дата: 07.11.10 15:58
Оценка:
Еще пара оценок:

In general, emitting a signal that is connected to some slots, is approximately ten times slower than calling the receivers directly, with non-virtual function calls. This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission), and to marshall any parameters in a generic fashion. While ten non-virtual function calls may sound like a lot, it's much less overhead than any new or delete operation, for example. As soon as you perform a string, vector or list operation that behind the scene requires new or delete, the signals and slots overhead is only responsible for a very small proportion of the complete function call costs.

The same is true whenever you do a system call in a slot; or indirectly call more than ten functions. On an i586-500, you can emit around 2,000,000 signals per second connected to one receiver, or around 1,200,000 per second connected to two receivers. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.

http://doc.trolltech.com/4.7/signalsandslots.html
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.