Results 1 to 10 of 10

Thread: Overhead when transmitting same signal to multiple threads via QueuedConnection

  1. #1
    Join Date
    Nov 2006
    Location
    Dresden, Germany
    Posts
    108
    Thanks
    9
    Thanked 12 Times in 10 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Question Overhead when transmitting same signal to multiple threads via QueuedConnection

    Hi all,

    I've connected a main thread's signal to the same slot of many objects (of same type) that all live in their own thread. I'm using QueuedConnection. The signal transports a potentially large argument (QJsonObject).

    Qt Code:
    1. signals:
    2. /*! Signal signature. */
    3. void sendData(QJsonObject o);
    To copy to clipboard, switch view to plain text mode 

    Actually, only one thread is the receiving thread and all the other threads simply ignore the message.

    From my understanding of Qt, the payload of the signal is copied over into the target thread's memory where it can be accessed from the target thread's slot without any locking mechanism.
    If I now copy the payload needlessly to many threads (all but one discard the message), I create an unnecessary overhead.

    Questions:
    1. is my expectation on Qt's behavior regarding copying of payload during queuing of the signal correct?
    2. is there an alternative design which allows me to emit the signal such, that it is only send to one of the connected threads? (i.e. connecting the target thread before emit and disconnecting afterwards would work, but has an overhead as well, right?)

    Thanks for any insights,
    Andreas
    Andreas

  2. #2
    Join Date
    Mar 2008
    Location
    Kraków, Poland
    Posts
    1,536
    Thanked 284 Times in 279 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    1. Who performs connect: sender or receiver ?
    2. Who decides whether to ignore data: sender or receiver ?

  3. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    If I now copy the payload needlessly to many threads (all but one discard the message), I create an unnecessary overhead.
    You are passing your QJsonObject instance by value: sendData( QJsonObject o ). In C++, this requires making a copy of the data. If you pass by reference instead: sendData( QJsonObject & o ) or sendData( const QJsonObject & o ) if the object will not be modified during the call, then you avoid copying.

    And as Lesiok asks, if you are in control of the connections, then why are you making connections to receivers who ignore the signal?
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Quote Originally Posted by d_stranz View Post
    if the object will not be modified during the call, then you avoid copying.
    In general this is true, but not in the case of a QueuedConnection.

    That mechanism requires that the signal arguments are stored in QVariant as the event being used for the delayed/cross-thread invocation uses that to store the arguments of the call.

    However, if we look at QJsonOject's copy constructor we see that it uses reference counting instead of copying the data itself.

    Which of course also means that none of the threads, including the main thread, should attempt any modification of the object as it is now shared data.

    Cheers,
    _

  5. The following 2 users say thank you to anda_skoa for this useful post:

    d_stranz (17th January 2019), ghorwin (18th January 2019)

  6. #5
    Join Date
    Nov 2006
    Location
    Dresden, Germany
    Posts
    108
    Thanks
    9
    Thanked 12 Times in 10 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Hi,

    thanks for the answers, let me clarify:

    @Lesiok:
    - sender performs connect, threads already exist and are started, QueuedConnection is created
    - all but one receivers (threads) rejects/ignores signal unless target matches

    @d_stranz:
    - copy by value was intended, otherwise we deal with shared data (as pointed out by anda_skoa) and I have to deal with blocking access to the shared object until receiver clears object. I wish to avoid potentially dangerous locking constructs by design.
    - the receivers are connected right after the objects are created (done dynamically at runtime based on runtime conditions)

    @anda_skoa:
    - thanks for looking into the constructor code - for some reason I was believing that Qt's object constructors perform a deep copy when passing data between threads. So, my current design does not have a performance overhead, but is risking race conditions/undefined behavior due to undetermined access to the shallow-copied JSonObject.


    My new solution:

    Create a deep copy once before emitting the signal
    Qt Code:
    1. // deep copy of QJsonObject stored in a document that resides in main thread's memory
    2. QJsonObject o = QJsonObject::fromVariantMap(document().object().toVariantMap());
    3. // safely send to other thread
    4. emit jsonAvailable(o);
    5. // when o goes out of scope, sending thread cannot access/disturb it anylonger, but the object remains and only receiving thread will access it.
    To copy to clipboard, switch view to plain text mode 

    Now the signal can be broadcasted to all listening threads and only the intended audience will actually look at the object.

    Any suggestions on how to improve that?

    Cheers,
    Andreas
    Last edited by ghorwin; 18th January 2019 at 13:44.
    Andreas

  7. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Thanks for the clarification.

    Any suggestions on how to improve that?
    Actually, this sounds pretty good. Making the deep copy protects the original, the copy will be passed with the least overhead, and if only one receiver will actually handle the signal, the copy cannot be corrupted.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  8. #7
    Join Date
    Jul 2012
    Posts
    244
    Thanks
    27
    Thanked 15 Times in 14 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Which of course also means that none of the threads, including the main thread, should attempt any modification of the object as it is now shared data.
    Does that mean a copy of reference-counted object is not actually threadsafe?

    If I have a QVector, and I passs a simple copy to another thread, that's a problem? I have to make sure that an actual deep copy is made?

  9. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    If I have a QVector, and I pass a simple copy to another thread, that's a problem? I have to make sure that an actual deep copy is made?
    QVectors have copy-on-write semantics: From the docs:

    QVector::QVector(const QVector<T> & other)
    Constructs a copy of other.
    This operation takes constant time, because QVector is implicitly shared. This makes returning a QVector from a function very fast. If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.

    and

    T & QVector:: operator[](int i)
    Returns the item at index position i as a modifiable reference.
    i must be a valid index position in the vector (i.e., 0 <= i < size()).
    Note that using non-const operators can cause QVector to do a deep copy.
    So if you pass a QVector by value to another thread, and that thread modifies it, then before the modification takes place a deep copy will be made. That means that the copy held by the thread will no longer the same as the original.

    There is a good article by Marc Mutz comparing STL and Qt containers, with an example about a third of the way down on how to modify containers in a thread-safe way.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  10. #9
    Join Date
    Nov 2006
    Location
    Dresden, Germany
    Posts
    108
    Thanks
    9
    Thanked 12 Times in 10 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Hi,

    so, we have now a "best practice" approach when dealing with shallow-copy Qt objects being passed between threads.

    First, create a deep copy of the object, connect the signal to the one receiving object, emit the signal and disconnect again. This way we handle ownership transfer and ensure that only one object is copied/accessed.

    Qt Code:
    1. // create deep copy of object as local object
    2. QJsonObject clonedObject = QJsonObject::fromVariantMap(o.toVariantMap());
    3. // create a queued connection to socket's thread
    4. connect( this, SIGNAL(sendJsonMessage(QJsonObject)),
    5. socket, SLOT(sendJsonObject(QJsonObject)),
    6. Qt::QueuedConnection);
    7. // transfer the data
    8. emit sendJsonMessage(clonedObject);
    9. // disconnect the signal
    10. disconnect( this, SIGNAL(sendJsonMessage(QJsonObject)),
    11. socket, SLOT(sendJsonObject(QJsonObject)));
    12.  
    13. // at end of function, local object goes out of scope and ownership is transfered to receiving thread
    To copy to clipboard, switch view to plain text mode 

    -Andreas
    Last edited by ghorwin; 4th June 2019 at 07:35.
    Andreas

  11. #10
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Overhead when transmitting same signal to multiple threads via QueuedConnection

    Why the connect and disconnect?

    Cheers,
    _

Similar Threads

  1. QNetworkAccessManager in multiple threads
    By mentalmushroom in forum Qt Programming
    Replies: 2
    Last Post: 8th August 2013, 00:14
  2. Qt socket consumes memory after transmitting
    By marco.stanzani in forum Qt Programming
    Replies: 2
    Last Post: 26th March 2012, 12:38
  3. Signal-Signal Connections Between Threads
    By PhilippB in forum Qt Programming
    Replies: 2
    Last Post: 15th December 2008, 19:27
  4. Waiting for multiple threads
    By Bebee in forum Qt Programming
    Replies: 1
    Last Post: 18th November 2008, 18:21
  5. QueuedConnection's and threads
    By gri in forum Qt Programming
    Replies: 14
    Last Post: 24th January 2007, 10:22

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.