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);
}
vlad
  • 193
  • 2
  • 10
  • Why don't you want to use C++11 threads instead? BTW, threads doesn't always mean faster. – vahancho May 13 '20 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. – vlad May 13 '20 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);`. – Azeem May 13 '20 at 10:55
  • Azeem - thank you - corrected. – vlad May 13 '20 at 10:57
  • @vlad: You're welcome! Better to use C++11's [range-for](https://en.cppreference.com/w/cpp/language/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](https://en.cppreference.com/w/cpp/thread/thread). But, you have to benchmark your single- and multi-threaded solutions for performance to figure out which one works better. – Azeem May 13 '20 at 11:07
  • Have you tried using `QtConcurrent::run`? If so what problems did you encounter? – G.M. May 13 '20 at 11:11
  • G.M. - No I haven't. I am not sure how, that is why I tried to ask here. – vlad May 13 '20 at 11:20

2 Answers2

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());
}
G.M.
  • 12,232
  • 2
  • 15
  • 18
  • 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. – pixelgrease Apr 19 '21 at 00:14
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;
}
pixelgrease
  • 1,940
  • 23
  • 26