大量使用信号和插槽会影响应用程序性能吗?

luc*_*0ni 30 qt signals-slots

问题只是出于教育目的:

在两个对象(例如两个线程)之间使用30-50或更多对信号和插槽是否会影响应用程序性能,运行时或响应时间?

Rei*_*ica 60

首先,你可能应该在QThreads中添加任何插槽.除了重新实现run方法和私有方法(不是信号!)之外,QThreads并不是真正意义上的.

QThread在概念上是一个线程控制器,而不是一个线程本身.在大多数情况下,您应该处理QObjects.启动一个线程,然后将对象实例移动到该线程.这是你在线程中正确使用插槽的唯一方法.将线程实例(它 QObject派生的!)移动到线程是一种黑客和糟糕的风格.尽管有不知情的论坛帖子,否则不要这样做.

至于你的其余问题:信号槽调用不必定位任何东西也不需要验证太多.建立连接时完成"位置"和"验证".通话时的主要步骤是:

  1. 从池中锁定信号槽互斥锁.

  2. 迭代连接列表.

  3. 使用直接或排队呼叫执行呼叫.

共同成本

任何信号槽调用始终作为moc生成的信号实现中的直接调用启动.在堆栈上构造信号的指向参数的数组.参数不会被复制.

然后信号调用QMetaObject::activate,其中获取连接列表互斥锁,并且迭代连接的插槽列表,为每个插槽发出呼叫.

直接连接

没有多少那里进行,通过直接调用槽被调用QObject::qt_static_metacall在已建立连接,或时间获得QObject::qt_metacall,如果QMetaObject::connect被用来建立连接.后者允许动态创建信号和插槽.

排队连接

参数必须编组和复制,因为调用必须存储在事件队列中并且信号必须返回.这是通过为复制分配一个指针数组,并在堆上复制每个参数来完成的.这样做的代码实际上是简单的旧C.

呼叫的排队是在内部完成的queued_activate.这是复制构造的地方.

排队调用的开销总是至少有一个堆分配QMetaCallEvent.如果调用有任何参数,则分配指向参数的数组,并为每个参数进行额外的分配.对于带n参数的调用,作为C表达式给出的成本是(n ? 2+n : 1)分配.阻塞调用的返回值是counter作为参数.可以说,Qt的这个方面可以优化到一切分配,但在现实生活中,只有你调用琐碎的方法才有意义.

基准测试结果

即使直接(非排队)信号槽调用也有可衡量的开销,但你必须选择你的战斗.易于构建代码与性能.您确实测量了最终应用程序的性能并确定了瓶颈,对吗?如果你这样做,你可能会发现,在现实生活中,信号槽开销不起作用.

信号槽机制唯一的时间有很大的开销,如果你正在调用琐碎的函数.比如说,如果您trivial在下面的代码中调用插槽.它是一个完整的,独立的基准测试,所以请随意运行它并亲眼看看.我的机器上的结果是:

Warming up the caches...
trivial direct call took 3ms
nonTrivial direct call took 376ms
trivial direct signal-slot call took 158ms, 5166% longer than direct call.
nonTrivial direct signal-slot call took 548ms, 45% longer than direct call.
trivial queued signal-slot call took 2474ms, 1465% longer than direct signal-slot and 82366% longer than direct call.
nonTrivial queued signal-slot call took 2474ms, 416% longer than direct signal-slot and 653% longer than direct call.
Run Code Online (Sandbox Code Playgroud)

也许应该注意的是,连接字符串非常快:)

请注意,我正在通过函数指针进行调用,这是为了防止编译器优化对add函数的直接调用.

//main.cpp
#include <cstdio>
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QElapsedTimer>
#include <QTextStream>

static const int n = 1000000;

class Test : public QObject
{
    Q_OBJECT
public slots:
    void trivial(int*, int, int);
    void nonTrivial(QString*, const QString&, const QString&);
signals:
    void trivialSignalD(int*, int, int);
    void nonTrivialSignalD(QString*, const QString&, const QString &);
    void trivialSignalQ(int*, int, int);
    void nonTrivialSignalQ(QString*, const QString&, const QString &);
private slots:
    void run();
private:
    void benchmark(bool timed);
    void testTrivial(void (Test::*)(int*,int,int));
    void testNonTrivial(void (Test::*)(QString*,const QString&, const QString&));
public:
    Test();
};

Test::Test()
{
    connect(this, SIGNAL(trivialSignalD(int*,int,int)),
            SLOT(trivial(int*,int,int)), Qt::DirectConnection);
    connect(this, SIGNAL(nonTrivialSignalD(QString*,QString,QString)),
            SLOT(nonTrivial(QString*,QString,QString)), Qt::DirectConnection);
    connect(this, SIGNAL(trivialSignalQ(int*,int,int)),
            SLOT(trivial(int*,int,int)), Qt::QueuedConnection);
    connect(this, SIGNAL(nonTrivialSignalQ(QString*,QString,QString)),
            SLOT(nonTrivial(QString*,QString,QString)), Qt::QueuedConnection);
    QTimer::singleShot(100, this, SLOT(run()));
}

void Test::run()
{
    // warm up the caches
    benchmark(false);
    // do the benchmark
    benchmark(true);
}

void Test::trivial(int * c, int a, int b)
{
    *c = a + b;
}

void Test::nonTrivial(QString * c, const QString & a, const QString & b)
{
    *c = a + b;
}

void Test::testTrivial(void (Test::* method)(int*,int,int))
{
    static int c;
    int a = 1, b = 2;
    for (int i = 0; i < n; ++i) {
        (this->*method)(&c, a, b);
    }
}

void Test::testNonTrivial(void (Test::* method)(QString*, const QString&, const QString&))
{
    static QString c;
    QString a(500, 'a');
    QString b(500, 'b');
    for (int i = 0; i < n; ++i) {
        (this->*method)(&c, a, b);
    }
}

static int pct(int a, int b)
{
    return (100.0*a/b) - 100.0;
}

void Test::benchmark(bool timed)
{
    const QEventLoop::ProcessEventsFlags evFlags =
            QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers;
    QTextStream out(stdout);
    QElapsedTimer timer;
    quint64 t, nt, td, ntd, ts, nts;

    if (!timed) out << "Warming up the caches..." << endl;

    timer.start();
    testTrivial(&Test::trivial);
    t = timer.elapsed();
    if (timed) out << "trivial direct call took " << t << "ms" << endl;

    timer.start();
    testNonTrivial(&Test::nonTrivial);
    nt = timer.elapsed();
    if (timed) out << "nonTrivial direct call took " << nt << "ms" << endl;

    QCoreApplication::processEvents(evFlags);

    timer.start();
    testTrivial(&Test::trivialSignalD);
    QCoreApplication::processEvents(evFlags);
    td = timer.elapsed();
    if (timed) {
        out << "trivial direct signal-slot call took " << td << "ms, "
               << pct(td, t) << "% longer than direct call." << endl;
    }

    timer.start();
    testNonTrivial(&Test::nonTrivialSignalD);
    QCoreApplication::processEvents(evFlags);
    ntd = timer.elapsed();
    if (timed) {
        out << "nonTrivial direct signal-slot call took " << ntd << "ms, "
               << pct(ntd, nt) << "% longer than direct call." << endl;
    }

    timer.start();
    testTrivial(&Test::trivialSignalQ);
    QCoreApplication::processEvents(evFlags);
    ts = timer.elapsed();
    if (timed) {
        out << "trivial queued signal-slot call took " << ts << "ms, "
               << pct(ts, td) << "% longer than direct signal-slot and "
               << pct(ts, t) << "% longer than direct call." << endl;
    }

    timer.start();
    testNonTrivial(&Test::nonTrivialSignalQ);
    QCoreApplication::processEvents(evFlags);
    nts = timer.elapsed();
    if (timed) {
        out << "nonTrivial queued signal-slot call took " << nts << "ms, "
               << pct(nts, ntd) << "% longer than direct signal-slot and "
               << pct(nts, nt) << "% longer than direct call." << endl;
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Test t;
    return a.exec();
}

#include "main.moc"
Run Code Online (Sandbox Code Playgroud)

  • 我建议看看关于如何真正明确使用QThread的非常清晰的教程http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation / (4认同)