0

I have a code like this with function double myfunction(double) that takes a lot of time to finish.

Using Qt (https://doc.qt.io/qt-5/qtconcurrentrun.html), how can I run the loop utilizing a thread for every myfunction call to have smaller computation time? Does this make sense?

std::vector<double> parameters;        //vector full of input values
std::vector<double> results;           //vector to store results in

for(uint i=0; i<parameters.size(); i++)
{
   double parameter = parameters.at(i);
   double result = myfunction(parameter);
   results.push_back(result);
}
7
  • Why don't you want to use C++11 threads instead? BTW, threads doesn't always mean faster. Commented May 13, 2020 at 10:52
  • C++ threads - I dont know it exists :-D. I thought running the function in parralel (ie. in threads) will be faster then one after each other. If my understanding is correct. Commented May 13, 2020 at 10:54
  • Isn't results empty at the start and you fill it as the result is computed? In this case, the loop won't execute i.e. i < results.size(). And, results.at(i) will throw an exception std::out_of_range if it is used when the vector is empty. Correction: i < parameters.size() and results.push_back(result);. Commented May 13, 2020 at 10:55
  • Azeem - thank you - corrected. Commented May 13, 2020 at 10:57
  • @vlad: You're welcome! Better to use C++11's range-for if you're iterating the whole vector. You can divide your workload and pass that to multiple threads to process and then aggregate the result. Take a look at C++11's std::thread. But, you have to benchmark your single- and multi-threaded solutions for performance to figure out which one works better. Commented May 13, 2020 at 11:07

2 Answers 2

2

A simple example usage of QtConcurrent::run for you own case would be something like (untested)...

std::vector<double> parameters;     // vector full of input values.
std::vector<double> results;        // Empty vector to store results.

/*
 * Each call to QtConcurrent::run returns a QFuture.  Store
 * these so we can wait on the results.
 */
std::vector<QFuture<double>> futures;

/*
 * Start all tasks and store the QFutures.
 */
for (auto parameter: parameters) {
    futures.push_back(QtConcurrent::run(&myfunction, parameter));
}

/*
 * Wait for each QFuture in turn and save its result.
 */
for (auto &f: futures) {
    results.push_back(f.result());
}
Sign up to request clarification or add additional context in comments.

1 Comment

G.M.'s solution works marvelously. If the number of parameters is less-than-or-equal-to the number of processor cores, the time to complete all is the duration of the longest execution of myFunction() because they all run in parallel. However, the second for-loop where the results are accumulated blocks until all results are computed. If called on the UI thread, the UI will freeze until the last result is computed.
0

Use QtConcurrent::mapped() and a QFutureWatcher so that all processing occurs asynchronously.

QFutureWatcher emits a finished() signal when all processing is complete. Results are accumulated by a single QFuture which is managed by the QFutureWatcher. Most importantly, the UI never freezes.

my_project.pro:

# must include "concurrent" to use QtConcurrent::mapped()
QT += concurrent
...

MappedProcessor.h:

#include <QObject>
#include <QFutureWatcher>
#include <vector>

class MappedProcessor : public QObject
{
    Q_OBJECT
public:
    explicit MappedProcessor(QObject *parent = nullptr);

public slots:
    void processValues();

private slots:
    void handleProcessingFinished();

private:
    static double myFunction(double value);

    QFutureWatcher<double> m_futureWatcher;
};

MappedProcessor.cpp:

#include "MappedProcessor.h"
#include <QtConcurrent/QtConcurrent>
#include <QList>
#include <QThread>

MappedProcessor::MappedProcessor(QObject *parent) : QObject(parent)
{
    connect(&m_futureWatcher, &QFutureWatcher<double>::finished,
            this, &MappedProcessor::handleProcessingFinished);
}

void MappedProcessor::processValues()
{
    // create some values to process
    std::vector<double> parameters;
    for (auto i = 0; i < 16; ++i)
        parameters.push_back(i);

    auto sequence = QVector<double>::fromStdVector(parameters);
    auto future = QtConcurrent::mapped(sequence, myFunction);
    m_futureWatcher.setFuture(future);
}

void MappedProcessor::handleProcessingFinished()
{
    // convert the results to std::vector<double>
    auto theResults = m_futureWatcher.future().results().toVector().toStdVector();

    qDebug() << "Received" << theResults.size() << "results";
}

// pretend to be a long running calculation...
double MappedProcessor::myFunction(double value)
{
    QThread::msleep(10000);
    return value;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.