Russian Qt Forum
Ноябрь 05, 2024, 22:07 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: QT 4.2.2 Медленная работа сигнал-слот  (Прочитано 13062 раз)
evilguard
Гость
« : Февраль 13, 2007, 01:34 »

Программа выполняет расчет, представляющий собой цикл из примерно 30000 итераций. Во время каждой итерации в QProgressBar посылается сигнал  для отображения хода расчета. Так вот - от этого программа замедлилась с 10 до 15 секунд, таким образом за 1 секунду выполняется 6000 связываний сингал-слот, хотя в документации написано что "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." А ведь процессор у меня далеко не 586 а Pentium 4 3.7 GHz(двухъядерный, кеша 4МБ).
То есть в доках либо явное вранье, либо я что-то не понял. Еще возможно что замедляет работу функция QProgressBar::setValue(). Кто сталкивался с этим?

добавлено спустя 52 минуты:

 В принципе необязательно конечно посылать сигнал после каждой итерации. Достаточно делать проверку условия и посылать сигнал, тогда когда увеличиваются сами проценты, тогда количество сигналов снизится с 33000 до 100. Так и правильнее будет. Но все-таки почему так медленно работает? В доках все в ажуре просто)
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #1 : Февраль 13, 2007, 11:19 »

33000 до 100 - естессно, правильней Улыбающийся
А медленно... ну... потому, что так Тролли сделали...
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Tonal
Гость
« Ответ #2 : Февраль 13, 2007, 11:23 »

Выброси всю работу из слота.
Бросай в пределах одного потока.
Тады скорость будет соответствовать.

P.S. Нафига обновлять прогресс на каждой итерации? Глазом всё равно не заметно - только систему грузишь зазря. ;-\
Обновляй на каждой 100ой - и горя не будешь знать. ;-)
Записан
evilguard
Гость
« Ответ #3 : Февраль 13, 2007, 17:01 »

Tonal, Racheengel
Ессесно я сделал так. Заморачиваюсь по поводу скорости работы сигнал- слот я по другой причине: пишу программу трехмерного моделирования, основные классы которой:
GLWidget
MainWindow
MObject.
Программа отображает объекты MObject с помощью GLWidget. Контейнер объектов MObject объявлен в теле MainWindow, по понятным причинам - оттуда же работают диалоги которые влияют на отолбражаемые объекты. Каждый объект рисуется функцией MObject::Draw(). Сцена может изменятся динамически, поэтому при каждом изменении положения камеры(меняется мышью) желательно перерисовывать сцену. Для этого GLWidget должен посылать постоянно сигнал о перерисовке в MainWindow. Боюсь это сильно уменьшит скорость работы..
Записан
SLiDER
Гость
« Ответ #4 : Февраль 13, 2007, 17:11 »

Где-то читал небольшую статейку, название что-то типа "Почему в Qt для сигнал-слотов не используются шаблоны". Так вот в ней приводился сравнительный анализ ресурсоемкости шаблонного и тролевого механизмов и было заявлено, что в случае с Qt один вызов сигнала эквивалентен 10 вызовам обычных функций. Но это было для трехи, в четверки механиз был не слабо переделан и я думаю что вряд ли в сторону ускорения. Так что если приложение критично к скорости, то местами, может оказаться, лучше вообще отказаться от сигнал слотов. И изобрести какой-нибудь велосипед попроще, но побыстрее.  :wink:
Записан
Dendy
Гость
« Ответ #5 : Февраль 15, 2007, 13:21 »

УвереньІ, что тормоза именно из-за вьІзова слота, а не из-за тела слота? Вообще вьІзов слота очень бьІстрьІй, Тролли не врут. Сравнительно медленно происходит именно связьІвать сигнала со слотом в методе QObject::connect().
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #6 : Февраль 15, 2007, 14:38 »

не используйте QProgressBar в time-cost приложениях ! Он ОЧЕНЬ медленный. Лучше напишите свой на онове QWidget+QPainter. Он будет работать в ~10 раз быстрее. Подмигивающий
Записан
evilguard
Гость
« Ответ #7 : Февраль 15, 2007, 20:41 »

Dendy, Alex_X
Нет конечно, connect вызывался перед расчетом. Скорее всего дело в теле слота, то есть в самом QProgressBar. Вот так вот.
Записан
Mr. Пронька
Гость
« Ответ #8 : Февраль 15, 2007, 22:23 »

Я думаю, такие обещания про миллионы срабатываний справедливы именно для самих срабатываний, без особых действий в обработчиках.
Достаточно сделать похожий проект в любой другой среде, хоть в том же Делфи. Засеки время выполнения чего-нибудь с отображением прогресса и без него. Сразу заметишь разницу. У меня в консольном приложении отображение прогресса выполнения замедляло цикл где-то в 50 раз.
Записан
Dendy
Гость
« Ответ #9 : Февраль 16, 2007, 12:05 »

Попробуйте так:

Код:
void MyObject::progress( float current )
{
  int percents = qRound( current/total*100 );
  if ( percents == oldPercents_ )
    return;
  oldPercents_ = percents;
  progressBar_->setValue( percents );
}
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #10 : Февраль 16, 2007, 14:49 »

это дублирование кода из QProgressBar. Так тоже есть проверки на текущее значение != новое значение. Дело не в значениях, а в том, что отрисовка текущего прогресса с помощью QStyle работает крайне медленно.
Записан
Dimchansky
Гость
« Ответ #11 : Февраль 18, 2007, 22:49 »

Я сделал замеры по числу отсылаемых сигналов.

Код:
#include <iostream>
#include <QObject>
#include <windows.h>

class Object1 : public QObject
{
  Q_OBJECT

public:
  void emitSignals(size_t N)
  for(size_t i=0; i<N; i++)
  {
      emit TestSignal();
  }

signals:
  void TestSignal();
};

class Object2 : public QObject
{
  Q_OBJECT

public slots:
  void SignalReceiver() {}
};

class PerformanceTimer
{
public:
PerformanceTimer()
: m_frequency(0)
, m_startTimeStamp(0)
, m_stopTimeStamp(0)
, m_totalTime(0)
, m_isHighResolution(false)
, m_resolution(0)
{
  if(::QueryPerformanceFrequency((PLARGE_INTEGER)&m_frequency))
  {
    m_isHighResolution = true;
  }
  else
  {
    m_isHighResolution = false;
    m_frequency = 1000;
  }
  m_resolution = static_cast<double>(1) / static_cast<double>(m_frequency);
}

void Start()             // start timer
{
  m_startTimeStamp = m_stopTimeStamp = GetTimeStamp();
}

void Stop()              // stop timer
{
  m_stopTimeStamp = GetTimeStamp();
  m_totalTime += m_stopTimeStamp-m_startTimeStamp;
}

void ResetTotalTime()    // set total time to 0
{
  m_totalTime = 0;
}

double Duration() const  // last Start/Stop duration in seconds
{
  return static_cast<double>(m_stopTimeStamp - m_startTimeStamp) *
         m_resolution;
}

double TotalTime() const // sum of all Start/Stop durations in seconds
{
  return static_cast<double>(m_totalTime) * m_resolution;
}

bool IsHighResolution() const
{
  return m_isHighResolution;
}

__int64 Frequency() const
{
  return m_frequency;
}

double Resolution() const
{
  return m_resolution;
}

private:
  __int64 m_frequency;
  __int64 m_startTimeStamp;
  __int64 m_stopTimeStamp;
  __int64 m_totalTime;
  bool m_isHighResolution;
  double m_resolution;

__int64 GetTimeStamp() const
{
  if(m_isHighResolution)
  {
    __int64 ticks = 0;
    ::QueryPerformanceCounter((PLARGE_INTEGER)&ticks);
    return ticks;
  }
  return ::GetTickCount();
}  
};


int main()
{
  Object1 obj1;
  Object2 obj2;

  QObject::connect(&obj1, SIGNAL(TestSignal()), &obj2, SLOT(SignalReceiver()));

  PerformanceTimer timer;

  const size_t N = 10000000;

  timer.Start();
  obj1.emitSignals(N);
  timer.Stop();

  std::cout << "Emited signals count: " << N << std::endl;
  std::cout << "Total time: " << timer.Duration() << std::endl;
  std::cout << "Signals per second: " << (size_t)((double)N/timer.Duration()) << std::endl;

  return 0;
}


На AMD Athlon 64 3000+ результаты:
Код:
Emited signals count: 10000000
Total time: 6.17696
Signals per second: 1618919


Если сделать два получателя:
Код:
int main()
{
  Object1 obj1;
  Object2 obj2;
  Object2 obj3;

  QObject::connect(&obj1, SIGNAL(TestSignal()), &obj2, SLOT(SignalReceiver()));
  QObject::connect(&obj1, SIGNAL(TestSignal()), &obj3, SLOT(SignalReceiver()));

  PerformanceTimer timer;

  const size_t N = 10000000;

  timer.Start();
  obj1.emitSignals(N);
  timer.Stop();

  std::cout << "Emited signals count: " << N << std::endl;
  std::cout << "Total time: " << timer.Duration() << std::endl;
  std::cout << "Signals per second: " << (size_t)((double)N/timer.Duration()) << std::endl;

  return 0;
}


То результаты такие:
Код:
Emited signals count: 10000000
Total time: 8.20403
Signals per second: 1218912


Т.е. при увеличении числа получателей в два раза, время увеличилось в 1,328 раза. Улыбающийся

добавлено спустя 7 минут:

 Кстати, если кто-то напищет PerformanceTimer для Linux, можно будет сравнить результаты. Улыбающийся
Записан
nova
Гость
« Ответ #12 : Февраль 20, 2007, 10:44 »

Вот наваял тестик Улыбающийся
Должен собиратся везде Улыбающийся
object1.h
Код:

#ifndef OBJECT1_H
#define OBJECT1_H

#include <QObject>
#include "object2.h"

/**
@author Alexander Novikov <nova@alba.ua>
*/
class Object1 : public QObject
{
Q_OBJECT
public:
  Object1(QObject *parent=0);

  ~Object1();
   void emitSignals(qint64 N);
   void callFunction(Object2* obj,qint64 N);
   void callFunction2(Object2* obj1,Object2* obj2,qint64 N);
  signals:
   void TestSignal();
};

#endif

object1.cpp
Код:

#include "object1.h"

Object1::Object1(QObject *parent)
 : QObject(parent)
{
}


Object1::~Object1()
{
}

void Object1::emitSignals(qint64 N)
{
  for(qint64 i=0; i<N; i++)
  {
    emit TestSignal();
  }
}



void Object1::callFunction(Object2 * obj, qint64 N)
{
  for(qint64 i=0; i<N; i++)
  {
    obj->SignalReceiver();
  }
}

void Object1::callFunction2(Object2 * obj1, Object2 * obj2, qint64 N)
{
  for(qint64 i=0; i<N; i++)
  {
    obj1->SignalReceiver();
    obj2->SignalReceiver();
  }
}

object2.h
Код:

#ifndef OBJECT2_H
#define OBJECT2_H

#include <QObject>


/**
@author Alexander Novikov <nova@alba.ua>
*/
class Object2 : public QObject
{
  Q_OBJECT
public:
  Object2(QObject *parent=0);

    ~Object2();
  public slots:
    void SignalReceiver();

};

#endif

object2.cpp
Код:

#include "object2.h"

Object2::Object2(QObject *parent)
  : QObject(parent)
{
}


Object2::~Object2()
{
}

void Object2::SignalReceiver()
{
}

testobhect.h
Код:

#ifndef TESTOBJECT_H
#define TESTOBJECT_H

#include <QObject>

class Object1;
class Object2;
/**
@author Alexander Novikov <nova@alba.ua>
*/
class TestObject : public QObject
{
Q_OBJECT
public:
    TestObject(QObject *parent = 0);

    ~TestObject();
    void runTest(qint64 N);
  private:
Object1 *obj1;
Object2 *obj2;


};

#endif

testobject.cpp
Код:

#include "testobject.h"
#include "object1.h"
#include "object2.h"

TestObject::TestObject(QObject *parent)
 : QObject(parent)
{
  obj1=new Object1(this);
  obj2=new Object2(this);
  connect( obj1, SIGNAL(TestSignal()), obj2, SLOT(SignalReceiver()));

}


TestObject::~TestObject()
{
}

void TestObject::runTest(qint64 N)
{
  obj1->emitSignals(N);
}

main.cpp
Код:

#include <QtCore>
#include <QDebug>
#include "object1.h"
#include "object2.h"
#include "testobject.h"

int main(int argc, char *argv[])
{
  QCoreApplication app(argc, argv);

  Object1 obj1;
  Object2 obj2;
  Object2 obj3;

  QObject::connect( &obj1, SIGNAL(TestSignal()), &obj2, SLOT(SignalReceiver()));

  QTime timer;
  double time;

  const qint64 N = 100000000;

  timer.start();
  obj1.callFunction(&obj2,N);
  time=(double)timer.elapsed()/1000;

  qDebug() << "objectCall count:" << N;
  qDebug() << "objectCall total time: " << time;
  qDebug() << "Call per second: " << (size_t)((double)N/time);

  timer.restart();
  obj1.callFunction2(&obj2,&obj3,N);
  time=(double)timer.elapsed()/1000;

  qDebug() << "objectCall2 count:" << N;
  qDebug() << "objectCall2 total time: " << time;
  qDebug() << "Call2 per second: " << (size_t)((double)N/time);

  timer.restart();
  obj1.emitSignals(N);
  time=(double)timer.elapsed()/1000;

  qDebug() << "Emited signals count: " << N;
  qDebug() << "Total time: " << time;
  qDebug() << "Signals per second: " << (size_t)((double)N/time);

  QObject::connect( &obj1, SIGNAL(TestSignal()), &obj3, SLOT(SignalReceiver()));

  timer.restart();
  obj1.emitSignals(N);
  time=(double)timer.elapsed()/1000;

  qDebug() << "On two slot connected";
  qDebug() << "Emited signals count: " << N;
  qDebug() << "Total time: " << time;
  qDebug() << "Signals per second: " << (size_t)((double)N/time);
 
  TestObject to;
  timer.restart();
  to.runTest(N);
  time=(double)timer.elapsed()/1000;

  qDebug() << "TestObject slot connected";
  qDebug() << "Emited signals count: " << N;
  qDebug() << "Total time: " << time;
  qDebug() << "Signals per second: " << (size_t)((double)N/time);
 

  return 0;
}

test.pro
Код:

SOURCES += main.cpp \
object1.cpp \
object2.cpp \
testobject.cpp
TEMPLATE = app
CONFIG += warn_on \
          qt \
          release
TARGET = test

QT -= gui
HEADERS += object1.h \
object2.h \
testobject.h
QT += core

мои резултаты следующие:
Цитировать

cpu: Intel(R) Core(TM)2 Duo CPU  6400@ 2.13GHz
Linux bear.alba.ua 2.6.19-1.2895.fc6 #1 SMP Wed Jan 10 18:50:56 EST 2007 x86_64 x86_64 x86_64 GNU/Linux
$ ./test
objectCall count: 100000000
objectCall total time:  0.339
Call per second:  294985250
objectCall2 count: 100000000
objectCall2 total time:  0.521
Call2 per second:  191938579
Emited signals count:  100000000
Total time:  42.859
Signals per second:  2333232
On two slot connected
Emited signals count:  100000000
Total time:  60.505
Signals per second:  1652755
TestObject slot connected
Emited signals count:  100000000
Total time:  40.296
Signals per second:  2481635

При работе теста грузится одно ядро.
Вывод: вызов signal->slot в 100 раз медленнее чем просто вызв метода !!!
Вот так вот Грустный

Я конечно понимаю что за удобство надо платить  :roll:
Может имеет смысл начать пинать ( желательно сильно ) тролей на предмет оптимизации ентого места в КуТе ?
Или всем скопом пооптимизировать  Крутой

P.S. Попробуйте у себя позапускать тестик на разных машинках, интересно получить сравнительный результат.

добавлено спустя 7 часов 42 минуты:

 На работе:
Цитировать

cpu: AMD Athlon(tm) @ 1252.851MHz
Linux bw.alba.ua 2.6.19-1.2911.fc6 #1 SMP Sat Feb 10 15:03:33 EST 2007 i686 athlon i386 GNU/Linux
$ ./test
objectCall count: 100000000
objectCall total time:  0.805
Call per second:  124223602
objectCall2 count: 100000000
objectCall2 total time:  1.212
Call2 per second:  82508250
Emited signals count:  100000000
Total time:  127.591
Signals per second:  783754
On two slot connected
Emited signals count:  100000000
Total time:  158.853
Signals per second:  629512
TestObject slot connected
Emited signals count:  100000000
Total time:  108.603
Signals per second:  920784


Улыбающийся
Записан
Dimchansky
Гость
« Ответ #13 : Февраль 20, 2007, 23:38 »

Athlon 64 3000+
Цитировать
objectCall count: 100000000
objectCall total time:  0.437
Call per second:  228832951
objectCall2 count: 100000000
objectCall2 total time:  0.688
Call2 per second:  145348837
Emited signals count:  100000000
Total time:  56.141
Signals per second:  1781229
On two slot connected
Emited signals count:  100000000
Total time:  81.671
Signals per second:  1224424
TestObject slot connected
Emited signals count:  100000000
Total time:  59.188
Signals per second:  1689531


только pro-файл под Windows я переделал, т.к. нифига не выводилось на экран и программа моментом выходила.

Код:
TEMPLATE = app

CONFIG += console
CONFIG += release
CONFIG += warn_on
TARGET = test

DESTDIR = ./

HEADERS += object1.h \
           object2.h \
           testobject.h

SOURCES += main.cpp \
           object1.cpp \
           object2.cpp \
           testobject.cpp
Записан
nova
Гость
« Ответ #14 : Февраль 21, 2007, 00:43 »

Я вот тут переделал тест под Qt-3.3 чуть в более простом виде
object1.h
Код:

#ifndef OBJECT1_H
#define OBJECT1_H

#include <qobject.h>

class Object2;
class Object1 : public QObject
{
Q_OBJECT
public:
    Object1(QObject *parent = 0, const char *name = 0);

    ~Object1();
    void emitSignals(int N);
    void callFunction(Object2* obj,int N);
  signals:
    void TestSignal();
};

#endif

object1.cpp
Код:

#include "object1.h"
#include "object2.h"

Object1::Object1(QObject *parent, const char *name)
 : QObject(parent, name)
{
}


Object1::~Object1()
{
}

void Object1::emitSignals(int N)
{
  for(int i=0; i<N; i++)
  {
    emit TestSignal();
  }
}

void Object1::callFunction(Object2 * obj, int N)
{
  for(int i=0; i<N; i++)
  {
    obj->SignalReceiver();
  }
}

object2.h
Код:

#ifndef OBJECT2_H
#define OBJECT2_H

#include <qobject.h>

class Object2 : public QObject
{
Q_OBJECT
public:
    Object2(QObject *parent = 0, const char *name = 0);

    ~Object2();
  public slots:
    void SignalReceiver();

};

#endif

object2.cpp
Код:

#include "object2.h"

Object2::Object2(QObject *parent, const char *name)
 : QObject(parent, name)
{
}

Object2::~Object2()
{
}

void Object2::SignalReceiver()
{
}

main.cpp
Код:

#include <qdatetime.h>

#include <qapplication.h>
#include "object1.h"
#include "object2.h"

int main( int argc, char ** argv ) {
    QApplication a( argc, argv );
    QTime timer;
    int N= 100000000;
    double time;
    Object1 obj1;
    Object2 obj2;
    Object2 obj3;
    QObject::connect(&obj1, SIGNAL(TestSignal()), &obj2, SLOT(SignalReceiver()));

    timer.start();
    obj1.callFunction(&obj2,N);
    time=(double)timer.elapsed()/1000;
    qDebug("Call count: %d",N);
    qDebug("Total time: %f",time);
    qDebug("Call per second: %d\n",(size_t)((double)N/time));

    timer.restart();
    obj1.emitSignals(N);
    time=(double)timer.elapsed()/1000;
    qDebug("Emited count: %d",N);
    qDebug("Total time: %f",time);
    qDebug("Signals per second: %d\n",(size_t)((double)N/time));

    qDebug("Connect obj3");
    QObject::connect(&obj1, SIGNAL(TestSignal()), &obj2, SLOT(SignalReceiver()));
    timer.restart();
    obj1.emitSignals(N);
    time=(double)timer.elapsed()/1000;
    qDebug("Emited count: %d",N);
    qDebug("Total time: %f",time);
    qDebug("Signals per second: %d\n",(size_t)((double)N/time));

    return 0;
}

test3.pro
Код:

SOURCES += main.cpp \
           object1.cpp \
           object2.cpp
TEMPLATE = app
CONFIG += release \
          warn_on \
          thread \
          qt
TARGET = test3
HEADERS += object1.h \
object2.h

на тойже машине результаты следуюшие
Цитировать

cpu: Intel(R) Core(TM)2 Duo CPU 6400@ 2.13GHz
Linux bear.alba.ua 2.6.19-1.2895.fc6 #1 SMP Wed Jan 10 18:50:56 EST 2007 x86_64 x86_64 x86_64 GNU/Linux
$ ./test3
Call count: 100000000
Total time: 0.291000
Call per second: 343642611

Emited count: 100000000
Total time: 3.193000
Signals per second: 31318509

Connect obj3
Emited count: 100000000
Total time: 5.629000
Signals per second: 17765144

Забавно не правдали?
По этим тестам выходит что в плане выполнения вызовов через сигтал-слот qt-4.2.2 более чем в ДЕСЯТЬ раз медленнее qt-3.3.7 которая в свою очередь, более чем в ДЕСЯТЬ раз медленнее чем простой выхов метода класса :shock:
Я конечно понимаю что за удобство в написании нужно платить, и я даже не против платить ДЕСЯТИКРАТНЫМ замедлением в этиз вызовах, но никак не замедлением в СТО РАЗ !!!!!!!!!!!!!!!
Хотя механизм ну ОЧЕНЬ удобный Улыбающийся
А строчку в документайии
Цитировать

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

троли похоже просто скопировали из qt-3.x.x  :roll:
Ребята, у кого с буржуйскими буквами в порядке, пните тролей этими тестами.
Я к стати поковырялся в исходниках/профилерах и выяснил что основное время тратится на QMutex::lock(), QMutex::unlock() и QHashData::nextNode(). Посмотрел на эти функции так и не понял почему Крутой
да и тройке такого не происходит Веселый
Я б и сам попинал, нолько у меня с ентим проблема( в смысле с буковками),понимать - все понимаю без проблем , а сказать толком нече не могу :oops:
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.136 секунд. Запросов: 22.