Java – multithreading代码在更多内核上运行速度更快

我只是在4核机器上运行一些multithreading代码,希望它比单核机器更快。 这是一个想法:我有一个固定数量的线程(在我的情况下,每个核心一个线程)。 每个线程都执行以下forms的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()) {} } 

添加更multithreading并不一定能保证提高性能。 使用其他线程可能会导致性能下降的原因有很多:

  • 粗粒度锁定可能过度序列化执行 – 也就是说,锁定可能导致一次只运行一个线程。 您可以获得多个线程的所有开销,但没有任何好处。 尽量减少锁的持有时间。
  • 这同样适用于过于频繁的障碍和其他同步结构。 如果内部j循环快速完成,您可能会将大部分时间花在屏障上。 尝试在同步点之间做更多工作。
  • 如果您的代码运行得太快,则可能没有时间将线程迁移到其他CPU核心。 除非你创建了许多非常短暂的线程,否则这通常不是问题。 使用线程池,或者只是让每个线程更多工作可以提供帮助。 如果你的线程每个运行超过一秒钟,这不太可能是一个问题。
  • 如果您的线程正在处理大量共享读/写数据,则缓存行反弹可能会降低性能。 也就是说,虽然这通常会导致性能下降,但仅凭这一点不太可能导致性能比单线程情况更差。 尝试确保每个线程写入的数据与其他线程的数据按缓存行的大小(通常约为64字节)分开。 特别是,没有像[thread A, B, C, D, A, B, C, D ...]那样布局的输出数组

由于您没有显示您的代码,我在这里无法详细说明。

你正在睡觉纳秒而不是毫秒

我改变了

 Thread.sleep(0, 100000 / numberOfThreads); // sleep 0.025 ms for 4 threads 

 Thread.sleep(100000 / numberOfThreads); 

并获得与正常情况下启动的线程数成比例的加速。


我发明了一个CPU密集型的“ countPrimes ”。 这里提供完整的测试代码。

我在四核机器上得到以下加速:

 4 threads: 1625 1 thread: 3747 

(CPU负载监视器确实显示前一种情况下4个进程忙,而后一种情况下1个核心正忙。)

结论:在同步之间,每个线程中的工作量相对较小 。 同步比实际的CPU密集型计算工作花费更多的时间。

(另外,如果你有内存密集型代码,比如线程中大量的数组访问,那么CPU无论如何都不会成为瓶颈,你不会通过将它分成多个CPU而看到任何加速。 )

runnable中的代码实际上并没有做任何事情。
在4个线程的具体示例中,每个线程将hibernate2.5秒并通过barier等待其他线程。
所以所发生的一切都是每个线程都在处理器上增加i然后阻塞睡眠,使处理器可用。
我不明白为什么调度程序会将每个线程分配到一个单独的核心,因为所有发生的事情都是线程大多等待。
期望仅使用相同的核心并在线程之间切换是公平合理的
UPDATE
刚刚看到你更新post说有些工作正在循环中发生。 虽然你不说,但是发生了什么。

跨核心同步比在单核上同步要慢得多

因为在单个核心机器上,JVM不会在每次同步期间刷新缓存(操作非常慢)

看看这篇博文

这是一个未经测试的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() { } }