18

Suppose your application needs to run a function in multiple threads the number of which is more than the number of CPU cores/threads. One way is to use QtConcurrent and setting the maximum thread count :

MyClass *obj = new MyClass;

QThreadPool::globalInstance()->setMaxThreadCount(30);

for(int i=0;i<30;i++)
    QtConcurrent::run(obj, &MyClass::someFunction);

Another way is to have multiple objects and move them to different threads using moveToThread :

for(int i=0;i<30;i++)
{
        MyClass *obj = new MyClass;
        QThread *th = new QThread();
        obj->moveToThread(th);
        connect(th, SIGNAL(started()), obj, SLOT(someFunction()) );
        connect(obj, SIGNAL(workFinished()), th, SLOT(quit()) );
        connect(th, SIGNAL(finished()), obj, SLOT(deleteLater()) );
        connect(th, SIGNAL(finished()), th, SLOT(deleteLater()) );

        th->start();
}

As the number of threads are more than the number of CPU cores, the threads should be switched between different cores when running.

The question is whether the two approaches have different performances or not? i.e does switching of a QThread differ from one that is run using QtConcurrent::run ?

Nejat
  • 31,784
  • 12
  • 106
  • 138
  • It depends on what each thread does. Rule of thumb: for CPU bound threads more threads than cores will decrease overall performance; for I/O bound threads it is possible to have more threads than available cores. You need to measure. – Richard Critten Jun 06 '15 at 07:55
  • @RichardCritten You are right, but the question here is the difference between `QtConcurrent` and `QThread`in switching time. – Nejat Jun 06 '15 at 07:59
  • Depends on your use case. As always, measure to see if it makes a difference in your situation. QConcurrent is convenience around QThreads, the differences will lie in how the tasks are scheduled and resources utilized, not in context switching times as such. Writing your own solution will give your more flexibility but of course also more work and more bugs. – Frank Osterfeld Jun 06 '15 at 08:12
  • @FrankOsterfeld But here all of the resource utilization, scheduling, .. are the same as 30 threads are running the same function. Only the approach of starting the threads are different. So I think the only difference would be how context switching differ between the two. – Nejat Jun 06 '15 at 08:23

2 Answers2

17

I agree with first answer, but I want to add something.

QThread is low-level class which just run OS-specific functions. What is the QtConcurrent? The answer is in Qt source code.

First level: run

QFuture<T> run(T (*functionPointer)())  
{
        return (new StoredFunctorCall0<T, T (*)()>(functionPointer))->start();
}

Second:

struct StoredFunctorCall0: public RunFunctionTask<T>    { ...

Third:

template <typename T>
class RunFunctionTaskBase : public QFutureInterface<T> , public QRunnable
{ ...

Now about QRunnable. When we start QRunnable with QThreadPool we do:

start() which calls tryStart() which calls startThread() which operate with QThreadPoolThread (and it is a QThread subclass) and it is finally call start() of QThread.

And of course this chain is not full, long road, isn't it? So as I know, when we use abstraction, we have abstraction penalty (QtConcurrent has bigger penalty then QThread), but final result is same, it is QThread.

Yunus Temurlenk
  • 4,085
  • 4
  • 18
  • 39
Jablonski
  • 18,083
  • 2
  • 46
  • 47
  • Thanks. So if they are all `QThread`s I guess there is not much difference between the two after they have started running. – Nejat Jun 06 '15 at 08:39
11

Short answer: it depends on the nature/logic of the workload.

QtConcurrent runs a pool of threads and it is a higher level API not well-suited to run a large number of blocking operations: if you do a lot of blocking operations you will soon end up draining the pool and having other requests queued. In that case QThread (a lower level construct) is probably better suited for the operation (each one represents a single thread).

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 1
    In the example i provided, the maximum number of threads in thread pool is set to a bigger number, so it would not be drained and all of the threads are run without queuing. Also the nature/logic of the process of the two approaches are the same and they are running the same piece of code. – Nejat Jun 06 '15 at 08:30
  • @Nejat The 'large number' is with respect to that maximum number you set. The nature of the workload is not specified in this question so there isn't enough info to provide any analysis. You'll have to benchmark it yourself. – Marco A. Jun 06 '15 at 08:33