2012-05-31 41 views

Trả lời

55

Trước hết, bạn nên có lẽ không đặt bất kỳ khe trong QThreads. QThreads không thực sự có nghĩa là có nguồn gốc từ khác hơn là reimplementing phương thức run và phương thức riêng (không phải tín hiệu!).

QThread là một bộ điều khiển chuỗi, không phải là chính một chuỗi. Trong hầu hết trường hợp, bạn nên đối phó với QObjects. Bắt đầu một chuỗi, sau đó di chuyển đối tượng đối tượng đến chủ đề đó. Đó là cách duy nhất bạn sẽ nhận được các khe hoạt động chính xác trong chuỗi. Di chuyển thể hiện luồng (nó Bắt nguồn từ QObject!) Cho chuỗi là kiểu hack và kiểu xấu. Đừng làm điều đó bất chấp các bài đăng trên diễn đàn không được thông báo nói cách khác.

Đối với phần còn lại của câu hỏi: cuộc gọi khe tín hiệu không phải xác định bất kỳ điều gì cũng như xác thực nhiều. "Vị trí" và "xác nhận" được thực hiện khi kết nối được thiết lập. Các bước chính được thực hiện tại thời điểm gọi là:

  1. Khóa đột biến vị trí tín hiệu từ một hồ bơi.

  2. Lặp lại qua danh sách kết nối.

  3. Thực hiện cuộc gọi bằng cách sử dụng cuộc gọi trực tiếp hoặc hàng đợi.

Chi phí chung

Bất kỳ cuộc gọi tín hiệu-khe luôn bắt đầu như một cuộc gọi trực tiếp trong việc thực hiện của tín hiệu được tạo ra bởi moc. Một mảng các đối số của tín hiệu được xây dựng trên ngăn xếp. Các đối số không được sao chép.

Tín hiệu sau đó gọi QMetaObject::activate, trong đó danh sách kết nối mutex được mua và danh sách các vị trí được kết nối được lặp lại, đặt cuộc gọi cho từng vị trí.

Connections Direct

Không có nhiều được thực hiện ở đó, các khe cắm được gọi bằng cách gọi trực tiếp QObject::qt_static_metacall thu được tại thời điểm kết nối được thành lập, hoặc QObject::qt_metacall nếu QMetaObject::connect được sử dụng để thiết lập kết nối. Cái sau cho phép dynamic creation of signals and slots.

Connections xếp hàng đợi

Những lập luận phải marshalled và sao chép, kể từ khi cuộc gọi phải được lưu trữ trong một hàng đợi sự kiện và các tín hiệu phải trả lại. Điều này được thực hiện bằng cách phân bổ một mảng các con trỏ tới các bản sao, và sao chép-consting mỗi đối số trên heap. Mã để làm điều đó thực sự không đơn giản là cũ C.

Việc xếp hàng cuộc gọi được thực hiện trong vòng queued_activate. Đây là nơi mà việc xây dựng bản sao được thực hiện.

Chi phí của cuộc gọi xếp hàng luôn là ít nhất một phân bổ heap là QMetaCallEvent. Nếu cuộc gọi có bất kỳ đối số nào, thì một mảng con trỏ tới đối số được cấp phát và một phân bổ bổ sung được thực hiện cho mỗi đối số. Đối với cuộc gọi với các đối số n, chi phí được cung cấp dưới dạng biểu thức C là phân bổ (n ? 2+n : 1). Giá trị trả về cho các cuộc gọi chặn là truy cập dưới dạng đối số. Có thể cho rằng, khía cạnh này của Qt có thể được tối ưu hóa xuống một phân bổ cho tất cả mọi thứ, nhưng trong cuộc sống thực nó chỉ là vấn đề nếu bạn đang gọi các phương pháp tầm thường.

Benchmark Kết quả

Ngay cả một trực tiếp (không xếp hàng đợi) gọi tín hiệu-khe có overhead đo lường được, nhưng bạn phải chọn trận đánh của bạn. Dễ kiến ​​trúc mã so với hiệu suất. Bạn đo lường hiệu suất của ứng dụng cuối cùng của bạn và xác định tắc nghẽn, phải không? Nếu bạn làm như vậy, bạn có thể thấy rằng trong các ứng dụng thực tế, chi phí đầu vào khe tín hiệu không có vai trò gì.

Cơ chế khe thời gian duy nhất có chi phí đáng kể là nếu bạn đang gọi các chức năng tầm thường. Giả sử, nếu bạn gọi đến số trivial trong mã bên dưới. Đó là một tiêu chuẩn hoàn chỉnh, độc lập, vì vậy hãy tự do chạy nó và tự mình xem. Các kết quả trên máy tính của tôi là:

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. 

gì cần lưu ý, có lẽ, là concatenating dây khá nhanh :)

Lưu ý rằng tôi đang làm các cuộc gọi thông qua một con trỏ hàm, điều này là để ngăn chặn trình biên dịch tối ưu hóa các cuộc gọi trực tiếp đến chức năng bổ sung.

//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 " << ts << "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" 
+4

Tôi khuyên bạn nên xem hướng dẫn rất rõ ràng về cách thực sự sử dụng dứt khoát QThread http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the- đầy đủ giải thích/ – linello

+0

Cô ấy nói có khá nhiều những gì tôi nói ở đây ... –

+0

Có một lỗi trong đoạn mã trên, anh ta thử nghiệm lần cuối in thời gian trôi qua (ts) của bài kiểm tra ở trên đó. – Richy

4

Tất nhiên chúng ảnh hưởng đến hiệu suất của ứng dụng, chủ yếu là do thời gian dành cho việc định vị đối tượng kết nối + xác nhận trạng thái đối tượng vị trí n. Plus one of the major advantage of signal-slot mechanism is they are type=safe allowing communication between objects, irrespective of type of object unlike callbacks.

So với gọi lại, tín hiệu và vị trí hơi chậm do tính linh hoạt tăng mà chúng cung cấp, mặc dù sự khác biệt cho các ứng dụng thực là không đáng kể. Nói chung, phát ra một tín hiệu được kết nối với một số khe, chậm hơn khoảng mười lần so với gọi trực tiếp cho người nhận, với các cuộc gọi chức năng không phải ảo. Đây là chi phí cần thiết để định vị đối tượng kết nối, để lặp lại một cách an toàn trên tất cả các kết nối (tức là kiểm tra các bộ thu tiếp theo không bị hủy trong khi phát xạ) và để sắp xếp bất kỳ tham số nào theo kiểu chung. Trong khi mười cuộc gọi chức năng không ảo có thể nghe có vẻ như rất nhiều, nó ít hơn nhiều so với bất kỳ hoạt động mới hoặc xóa, ví dụ. Ngay sau khi bạn thực hiện một chuỗi, vectơ hoặc danh sách hoạt động đằng sau cảnh yêu cầu mới hoặc xóa, các tín hiệu và khe trên đầu chỉ chịu trách nhiệm cho một tỷ lệ rất nhỏ của chi phí cuộc gọi hàm đầy đủ.

Nguồn: Signals and Slots

+0

Bạn có chắc chắn rằng hệ số trên không thấp? Tín hiệu gọi QMetaObject :: kích hoạt, trong đó có khoảng một trăm dòng mã. Tôi đoán nó chậm hơn 100 lần so với cuộc gọi không trực tiếp ảo của khe. Nhưng tôi đồng ý với bạn: Trong hầu hết các trường hợp, chi phí này là không đáng kể. – leemes

+0

Chi phí của một hoạt động mới hoặc xóa trên một bộ cấp phát bộ nhớ hiện đại trong một ứng dụng đơn luồng là nhỏ. Rất nhỏ.Vì vậy, nhỏ trong thực tế, mà ghép hai 1000 ký tự QStrings vào một QString mới mất khoảng thời gian nhiều như là một kết nối trực tiếp khe cắm tín hiệu trên không! –

Các vấn đề liên quan