我只是在4核机器上运行一些多线程代码,希望它比单核机器更快。这是一个想法:我有一个固定数量的线程(在我的情况下,每个核心一个线程)。每个线程都执行以下形式的Runnable
:
private static int[] data; // data shared across all threads
public void run() {
int i = 0;
while (i++ < 5000) {
// do some work
for (int j = 0; j < 10000 / numberOfThreads) {
// each thread performs calculations and reads from and
// writes to a different part of the data array
}
// wait for the other threads
barrier.await();
}
}
在四核计算机上,此代码使用4个线程执行更糟比使用1个线程执行更糟。即使有CyclicBarrier
的开销,我也会认为代码的执行速度至少要快2倍。为什么它慢?
public void run() {
// do work
synchronized (this) {
if (atomicInt.decrementAndGet() == 0) {
atomicInt.set(numberOfOperations);
for (int i = 0; i < threads.length; i++)
threads[i].interrupt();
}
}
while (!Thread.interrupted()) {}
}
答案 0 :(得分:10)
添加更多线程并不一定能保证提高性能。使用其他线程可能会导致性能下降的原因有很多:
j
循环快速完成,您可能会将大部分时间花在屏障上。尝试在同步点之间做更多的工作。[thread A, B, C, D, A, B, C, D ...]
由于您尚未显示您的代码,因此我无法在此处详细说明。
答案 1 :(得分:4)
你正在睡觉纳秒而不是毫秒。
我改变了
Thread.sleep(0, 100000 / numberOfThreads); // sleep 0.025 ms for 4 threads
到
Thread.sleep(100000 / numberOfThreads);
并且与正在开始的线程数成比例加速。
我发明了一个CPU密集型的“countPrimes
”。完整的测试代码here。
我在四核机器上获得了以下加速:
4 threads: 1625
1 thread: 3747
(CPU负载监视器确实显示前一种情况下4个进程正忙,后一种情况下1个核心正忙。)
结论:您在同步之间的每个线程中执行相对较小的部分工作。同步比实际的CPU密集型计算工作花费的时间多得多。
(另外,如果您有内存密集型代码,例如线程中大量的数组访问,那么CPU无论如何都不会是瓶颈,你也看不到任何代码通过在多个CPU上拆分来加速。)
答案 2 :(得分:2)
runnable中的代码实际上没有做任何事情
在4个线程的具体示例中,每个线程将休眠2.5秒,并通过barier等待其他线程
所以所发生的一切都是每个线程都在处理器上增加i
,然后阻塞睡眠,让处理器可用。
我不明白为什么调度程序会将每个线程分配到一个单独的核心,因为所有发生的事情都是线程大多等待。
期望仅使用相同的核心并在线程之间切换是公平合理的
的更新强>
刚刚看到你更新帖子说有些工作正在循环中发生。虽然你没有说,但是发生了什么。
答案 3 :(得分:2)
答案 4 :(得分:1)
这是一个未经测试的SpinBarrier,但它应该可以工作。
检查案件是否有任何改善。由于您在循环中运行代码,因此如果您的内核处于空闲状态,则只会影响性能。 顺便说一句,我仍然相信你在计算,记忆密集的操作中有一个错误。你能告诉 你使用什么CPU + OS。
编辑,忘了版本。
import java.util.concurrent.atomic.AtomicInteger;
public class SpinBarrier {
final int permits;
final AtomicInteger count;
final AtomicInteger version;
public SpinBarrier(int count){
this.count = new AtomicInteger(count);
this.permits= count;
this.version = new AtomicInteger();
}
public void await(){
for (int c = count.decrementAndGet(), v = this.version.get(); c!=0 && v==version.get(); c=count.get()){
spinWait();
}
if (count.compareAndSet(0, permits)){;//only one succeeds here, the rest will lose the CAS
this.version.incrementAndGet();
}
}
protected void spinWait() {
}
}