gpt4 book ai didi

qt - 大量使用信号和槽是否会影响应用程序性能?

转载 作者:行者123 更新时间:2023-12-02 09:36:27 36 4
gpt4 key购买 nike

这个问题只是出于教育目的:

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

最佳答案

首先,您可能在QThreads 中放置任何槽。 QThreads 实际上并不是通过重新实现 run 方法和私有(private)方法(不是信号!)来派生的。

QThread 从概念上讲是一个线程 Controller ,而不是线程本身。在大多数情况下,您应该处理 QObject。启动一个线程,然后将对象实例移动到该线程。这是让插槽在线程中正常工作的唯一方法。将线程实例(它是 QObject 派生的!)移动到线程是一种 hack 和糟糕的风格。尽管不知情的论坛帖子另有说明,但不要这样做。

至于你的问题的其余部分:信号槽调用不必定位任何东西,也不必验证太多。 “定位”和“验证”是在连接建立时完成的。调用时完成的主要步骤是:

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

  2. 迭代连接列表。

  3. 使用直接调用或排队调用执行调用。

共同成本

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

信号然后调用QMetaObject::activate ,其中获取连接列表互斥体,并迭代连接的槽列表,为每个槽进行调用。

直接连接

这里没有做太多事情,通过直接调用建立连接时获得的QObject::qt_static_metacall来调用槽,或者如果是QObject::qt_metacall QMetaObject::connect 用于设置连接。后者允许 dynamic creation of signals and slots .

排队连接

必须对参数进行编码和复制,因为调用必须存储在事件队列中并且信号必须返回。这是通过分配一个指向副本的指针数组,并在堆上复制每个参数来完成的。执行此操作的代码实际上是简单的老式 C 代码。

调用的排队在 queued_activate 内完成。这是复制构造完成的地方。

排队调用的开销始终至少是 QMetaCallEvent 的一次堆分配。如果调用有任何参数,则会分配一个指向参数的指针数组,并为每个参数进行额外的分配。对于使用 n 参数的调用,以 C 表达式形式给出的成本为 (n ? 2+n : 1) 分配。阻塞调用的返回值是作为参数的计数器。可以说,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.

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

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

//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"

关于qt - 大量使用信号和槽是否会影响应用程序性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10838013/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com