所有
我写了一个小案例来测试多线程生产者/消费者模型。我的测试平台是一台低性能PC(8G RAM,J1900 CPU,4核)。我为Linux内核隔离了核心0,核心1-3未使用。生产者线程在核心1上运行,分配5000000个小对象,将它们放入全局队列。使用者线程在核心2上运行并从队列中释放对象。但我发现如果我没有设置它们的CPU亲和性(也就是说,它们运行在相同的核心0上),那么时间性能会比设置CPU亲和力(8.76s VS 14.66s)更好。测试结果保持相似。有人可以解释一下我的原因吗?如果我的前提不正确("设置CPU亲和力可以改善多线程进程'性能"),它至少应该不会变得更糟。我的代码片段如下:
void producer() {
Timestamp begin;
for ( int i = 0; i<data_nb; ++i ) {
Test* test = new Test(i, i+1);
queue.enqueue(test);
}
Timestamp end;
TimeDuration td = end-begin;
printf("producer: %ldms(%.6fs)\n", td.asMicroSecond(), td.asSecond());
}
void consumer() {
Timestamp begin;
do {
Test* test = queue.dequeue();
if ( test ) {
nb.add(1); // nb is an atomic counter
delete test;
test = nullptr;
}
} while ( nb.get() < data_nb );
Timestamp end;
TimeDuration td = end-begin;
//printf("%d data consumed\n", nb.get());
printf("consumer: %ldms(%.6fs)\n", td.asMicroSecond(), td.asSecond());
}
答案 0 :(得分:5)
从CPU亲和力中获取性能并不像将线程1推送到核心1而线程2推送到核心2那么简单。这是一个复杂且经过深入研究的主题,我将触及亮点。
首先,我们需要定义&#39;表现&#39;。通常,我们对throughput,latency和/或scalability感兴趣。将这三者结合起来是一个棘手的架构问题,在电信,金融和其他行业受到了严格的审查。
您的案例似乎是由吞吐量指标驱动的。我们希望跨线程的挂钟时间总和最小。陈述问题的另一种方式可能是,&#34;影响多线程进程吞吐量的因素有哪些?&#34;
以下是一些因素:
将这些想法应用到您的示例中,我看到了一些问题。我假设您有两个独立的线程并行运行。一个线程产生(新),另一个消耗(删除)。
堆是串行的。在不同的线程中调用new和delete是一个已知的性能瓶颈。有几个小块并行分配器可用,包括Hoard。
队列可能是串行的。没有显示实现,但是enqueue / dequeue可能是两个线程之间的序列化点。有许多lock free ring buffers可以在多个线程之间使用的例子。
线程饥饿。在该示例中,如果生产者比消费者慢,那么消费者将在大部分时间内闲置。这是编制高性能算法时必须考虑的队列理论的一部分。
在所有这些背景下,我们现在可以得出结论,在序列化和饥饿问题得到解决之前,线程亲和力可能不重要。事实上,这两个线程可能运行速度较慢,因为它们相互竞争共享资源或者只是浪费cpu时间空闲。结果,整体吞吐量下降,因此挂钟时间上升。
对于了解这些算法的工程师来说,行业需求巨大。教育自己可能是一个有利可图的冒险。