私は、ジョブが待ち行列にプッシュされるのを待っているスレッドプールをスレッドプールに持っています。C++:スレッドプールとコンテキスト切り替えのスローダウン
私は、1000のジョブをプールのキューに順番に追加するメインアプリケーションスレッドにループを持っています(ジョブを追加し、ジョブが終了するのを待ってから別のジョブx1000を追加します)。実際の並列処理が起こっていないので...ここにいくつかの擬似コードです:
////threadpool:
class ThreadPool
{
....
std::condition_variable job_cv;
std::condition_variable finished_cv;
std::mutex job_mutex;
std::queue<std::function <void(void)>> job_queue;
void addJob(std::function <void(void)> jobfn)
{
std::unique_lock <std::mutex> lock(job_mutex);
job_queue.emplace(std::move(jobfn));
job_cv.notify_one();
}
void waitForJobToFinish()
{
std::unique_lock<std::mutex> lock(job_mutex);
finished_cv.wait(lock, [this]() {return job_queue.empty(); });
}
....
void threadFunction() //called by each thread when it's first started
{
std::function <void(void)> job;
while (true)
{
std::unique_lock <std::mutex> latch(job_mutex);
job_cv.wait(latch, [this](){return !job_queue.empty();});
{
job = std::move(job_queue.front());
job_queue.pop();
latch.unlock();
job();
latch.lock();
finished_cv.notify_one();
}
}
}
}
...
////main application:
void jobfn()
{
//do some lightweight calculation
}
void main()
{
//test 1000 calls to the lightweight jobfn from the thread pool
for (int q = 0; q < 1000; q++)
{
threadPool->addJob(&jobfn);
threadPool->waitForJobToFinish();
}
}
だから基本的には、ジョブがキューとメインループに追加されている何が起こっているかを待つために開始されます
待機スレッドがそれをピックアップし、スレッドが終了すると、メインループが続行可能であり、キューに他のジョブを追加できるなどの通知をアプリケーションに送信します。そのようにして1000ジョブは順次処理されます。
ジョブ自体は非常に小さく、数ミリ秒で完了することができます。
しかし、私は奇妙な何か....
それが完了するのループにかかる時間は、本質的に、nは、スレッドプール内のスレッド数であるO(n)が気づきました。したがって、すべてのシナリオでジョブが1つずつ処理されても、10スレッドプールは1スレッドプールよりも1000ジョブのジョブを完了するのに10倍の時間がかかります。
私は理由を理解しようとしていますが、私の唯一の推測は、これまでのコンテキスト切り替えがボトルネックであることです...多分、1つのスレッドがジョブを取得しているときにコンテキスト切り替えのオーバーヘッドが必要になります。しかし、10スレッドが一度に1つのジョブを処理するために連続して回っているときには、余分な処理が必要ですか?しかし、それは私には意味がありません...スレッドB、C、Dのように、ジョブのスレッドAをアンロックするのに必要な操作と同じではないでしょうか?別のスレッドがそれを与えられるまでスレッドがコンテキストを失わないOSレベルのキャッシュがありますか?だから、同じスレッドを繰り返し呼び出すのは、スレッドA、B、Cを連続して呼び出すよりも速いのですか?
しかし、これは完全な推測です...他の人がなぜ私がこれらの結果を得ているかについていくつかの洞察を払う可能性があります...直感的に私はただ1つのスレッドが一度に実行されている限り、任意の数のスレッドを持つスレッドプールを持つことができ、[x]ジョブの合計タスク完了時間は同じになります(各ジョブが同一で、ジョブの総数が同じである限り)...なぜですかそれは間違っている?
これは関連していない可能性がありますが、コアの数はいくつですか? – merlin2011
@ merlin2011 16(2 CPU x 8コア) – Tyson
また、これをどのようにベンチマークしていますか?つまり、開始時と終了時の時間を測定しているか、各タスクのデータを収集しているので、超低速の10秒ごとに1であるかどうかを確認できますか? – merlin2011