1

重い計算を行う必要がある状況があります。データを細分化してからマージするのが最も速いことがわかりました (サイズが大きくなるにつれて、時間も速くなるため、分割は論理的です)。

アプリケーションにデータサイズを与えることができるはずです。たとえば、100 万個の double 値としましょう。

私が今持っているのは、このサイズに基づいて作成されたデータを関数に送信し、計算後にそれを返し、リターンをループしてこのデータをメインベクトルにアンロードすることです。

200 個のパーツを送りたいのですが、「最後の」パーツは 1 つです。たとえば、size = 1000005 を指定すると、この関数が最初に 5000 回実行され、次にサイズ 5 のデータを持つ最後の関数が実行されます。

int size = 1000000;
int times = size / 200; // 5000
int leftover = size % 200; // 0, this not performed

QVector<double> x(size);
QVector<double> y(size);

x = createData(size);
x = createData(size);

for (int i = 0; i < times; i++)
{
    holder = createData(200);
    QVector<double> tempx = x.mid(i*200, 200);
    QVector<double> tempy = y.mid(i*200, 200);
    holder = myfunction(tempx, tempy, 200);  // let it now just return `tempy`
    for (int j = 0; j < 200; j++)
    {
        y[i*200 + j] = holder[j];
    }
}
// leftover function here, really similar to this part before.

// plotting function here

最後に、x初期化されyたままになり、計算が行われます。

これらのコード部分は別々に実行でき、速度が重要であるため、複数のコアを使用したいと考えています。

以下は、状況をさらに特徴付けます。

  • これらの関数呼び出しは互いに独立しており、最終的にベクトルが完成したときにのみ、結果をプロットしたいと思います。
  • 各通話の完了時間は大きく異なります。
  • の量はtimes可変でなければなりません。

あまりにも多くのスレッドを使用するとプロセスが遅くなる可能性があるため、最大スレッド数は (少なくとも出発点として) コアの量にすることをお勧めします。状況を考えると、キューイングシステム/スレッドプールは、1 つのスレッドがいくつかの簡単なジョブを実行し、他のスレッドがより難しいジョブによってすべてを遅くしている間、時間を無駄にしないようにするのが理にかなっているように思われます。

数十のチュートリアルでいくつかの (通常は 2 つの) スレッドを使用していくつかのメッセージを出力するのは簡単に思えますが、ベクトルを返し、これらのスレッドをメイン関数に安全にアンロードする方法、およびスレッドプールを作成する方法について、より詳細なヘルプを提供してください。無駄にならない?

Ubuntu 13.04、Qt、および C++11x を使用していますが、問題にはなりません。

4

3 に答える 3

4

まず第一に、トレッドプールを書くのは難しいです。作成方法を本当に学びたい場合は、Antony Williams によって書かれた C++ Concurrency in Action という本で、それを達成する方法を説明しています。

ただし、あなたのケースは、単純な parallel_for が完全に適合する状況のようです。したがって、 Intel Threading Building Blocks ライブラリを使用することをお勧めします。このライブラリの利点は、非常に優れたスレッド プールがあり、C++11 の機能とうまく連携することです。

コード例:

#include "tbb/task_scheduler_init.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include "tbb/tbb_thread.h"
#include <vector>

int main() {
  tbb::task_scheduler_init init(tbb::tbb_thread::hardware_concurrency());
  std::vector<double> a(1000);
  std::vector<double> c(1000);
  std::vector<double> b(1000);

  std::fill(b.begin(), b.end(), 1);
  std::fill(c.begin(), c.end(), 1);

  auto f = [&](const tbb::blocked_range<size_t>& r) {
    for(size_t j=r.begin(); j!=r.end(); ++j) a[j] = b[j] + c[j];    
  };
  size_t hint_number_iterations_per_thread = 100;
  tbb::parallel_for(tbb::blocked_range<size_t>(0, 1000, hint_number_iterations_per_thread), f);
  return 0;
}

終わり!Intel TBB には、各スレッドのワークロードを調整しようとする非常に優れたスレッド プールがあります。Hint_number_iterations_per_thread が狂った数でない限り、最適解に非常に近いでしょう。

ところで: intel TBB は、ほとんどのコンパイラで動作するオープン ソース ライブラリです!

于 2013-08-31T22:57:14.230 に答える
0

QThreadPool を作成して QRunabble を拡張するよりも簡単に、QtConcurrentライブラリを使用できます。具体的QtConcurrent::mappedには、begin イテレーターと end イテレーターを受け取る関数、および関数 (ラムダの場合もあります) を使用し、スレッド プールの作成と実行を内部的に処理します。

2 つのバリエーションがあります。「mapped」は結果に QFuture を返しますが、現在のスレッドをブロックしませんが、「blockingMapped」は結果のリストを直接返します。

整数の大きなベクトルを 2 乗するには、次のようにします。

std::vector<int> myInts = ....

QVector<int> result = QtConcurrent::blockingMapped(myInts.begin(), myInts.end(), [](int x) { return x*x}; });
于 2014-04-25T18:11:10.910 に答える