C++ 如何提高将数据推送到互斥锁队列的性能
我有一个“作业”队列(函数指针和数据)从主线程推送到它上面,然后通知工作线程弹出数据并运行它 这些函数非常基本,如下所示:C++ 如何提高将数据推送到互斥锁队列的性能,c++,multithreading,optimization,C++,Multithreading,Optimization,我有一个“作业”队列(函数指针和数据)从主线程推送到它上面,然后通知工作线程弹出数据并运行它 这些函数非常基本,如下所示: class JobQueue { public: // usually called by main thread but other threads can use this too void push(Job job) { { std::lock_guard<std::mutex> lock(mute
class JobQueue {
public:
// usually called by main thread but other threads can use this too
void push(Job job) {
{
std::lock_guard<std::mutex> lock(mutex); // this takes 40% of the thread's time (when NOT sync'ing)
ready = true;
queue.emplace_back(job);
}
cv.notify_one(); // this also takes another 40% of the thread's time
}
// only called by worker threads
Job pop() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&]{return ready;});
Job job = list.front();
list.pop_front();
return job;
}
private:
std::list<Job> queue;
std::mutex mutex;
std::condition_variable cv;
bool ready;
};
(所有工作线程都有相同的模式,绿色表示执行,红色表示等待同步)我建议使用事件处理程序 事件有两种类型:
- 新工作来了
- 工人完成工作
您还需要一个事件处理程序。这些都是很棘手的,所以最好使用经过良好测试的库,而不是使用自己的库。我使用boost::asioTL;DR:在每项任务中做更多的工作。(每次可能会从队列中删除多个当前任务,但还有许多其他可能性。) 你的任务(计算)太小了。4x4矩阵乘法只是几个倍数和加法~60-70次行动。其中20个一起完成并不贵,大约1500个(流水线)算术运算。线程切换的成本(包括唤醒一个等待cv的线程,然后是实际的上下文切换)可能比这要高——可能要高得多 此外,同步的成本(互斥和cv的操作)非常昂贵,特别是在争用的情况下,尤其是在硬件本机同步操作比算术操作昂贵得多的多核系统上(因为多核之间的缓存一致性强制) 这就是为什么你观察到,当每个任务执行100个矩阵运算时,问题会减少,从20个矩阵运算增加到100个矩阵运算:工人们太频繁地回到井里做更多的事情,导致了争用,而他们只有20个MMs要做。。。给他们100个任务可以让他们放慢速度,从而减少争用
(在一条评论中,您指出只有一个供应商,这基本上消除了作为队列争用源的供应商。但即使在那里,在cv锁下,可以排队的任务数量越多越好-达到阻止工人执行任务的限制。)批量处理作业。与其一次增加一份工作,不如一次增加一整批工作。使用每工作者作业队列,并让生成每个作业的主线程将其添加到每工作者作业队列。还有许多其他可能的变化,这一切都取决于个人情况。当存在争用时,条件变量是多余的。如果在
pop()
@Chad()中抓取锁时ready为true,则不要wait
-哦,我以为它在尝试等待之前检查了谓词。我尝试在其周围添加另一个检查,但遗憾的是没有任何改进。@SamVarshavchik-我将尝试添加每个工作人员的作业队列。我最初避免了它,因为它使加入变得更加困难,但在这种情况下,它可能是值得的。批处理too1)您有什么理由必须扩展到96个线程吗?为什么不使用一个线程池,它的线程数与可用的内核数相同?2) 一个作业需要多少毫秒?如果作业非常短,那么最好进入无锁队列,而不是使用重载互斥体/cv同步。
Time to calc 2000000 matrice rotations
(20 rotations x 100000 jobs)
threads 0: 149 ms << no-bool baseline
threads 1: 151 ms << single threaded w/pool
threads 2: 89 ms
threads 3: 120 ms
threads 4: 216 ms
threads 8: 269 ms
threads 12: 311 ms << hardware hint
threads 16: 329 ms
threads 24: 332 ms
threads 96: 336 ms