我正在尝试一些多线程构造,但不知何故,似乎多线程并不比单个线程快。我把它缩小到一个非常简单的测试,使用嵌套循环(1000x1000),系统只计算
下面我发布了单线程和多线程的代码以及它们的执行方式
结果是单个线程在 110 ms 内完成循环,而两个线程也需要 112 ms 。
我不认为问题是多线程的开销。如果我只将两个Runnable中的一个提交给ThreadPoolExecutor,它会在单个线程的一半时间内执行,这是有道理的。但是添加第二个Runnable会让它慢10倍。两个3.00Ghz核心都运行100%
我认为它可能是特定于PC的,因为其他人的PC在多线程上显示了双倍速度的结果。但那么,我能做些什么呢?我有一台Intel Pentium 4 3.00GHz(2个CPU)和Java jre6。
测试代码:
// Single thread:
long start = System.nanoTime(); // Start timer
final int[] i = new int[1]; // This is to keep the test fair (see below)
int i = 0;
for(int x=0; x<10000; x++)
{
for(int y=0; y<10000; y++)
{
i++; // Just counting...
}
}
int i0[0] = i;
long end = System.nanoTime(); // Stop timer
此代码的执行时间约为 110 ms 。
// Two threads:
start = System.nanoTime(); // Start timer
// Two of the same kind of variables to count with as in the single thread.
final int[] i1 = new int [1];
final int[] i2 = new int [1];
// First partial task (0-5000)
Thread t1 = new Thread() {
@Override
public void run()
{
int i = 0;
for(int x=0; x<5000; x++)
for(int y=0; y<10000; y++)
i++;
i1[0] = i;
}
};
// Second partial task (5000-10000)
Thread t2 = new Thread() {
@Override
public void run()
{
int i = 0;
for(int x=5000; x<10000; x++)
for(int y=0; y<10000; y++)
i++;
int i2[0] = i;
}
};
// Start threads
t1.start();
t2.start();
// Wait for completion
try{
t1.join();
t2.join();
}catch(Exception e){
e.printStackTrace();
}
end = System.nanoTime(); // Stop timer
此代码在 112 ms 中执行。
编辑:我将Runnables更改为Threads并删除了ExecutorService(为了简化问题)。
修改:尝试了一些建议
答案 0 :(得分:11)
你绝对不想继续轮询Thread.isAlive()
- 这没有充分的理由会耗费大量的CPU周期。请改用Thread.join()
。
此外,让线程直接递增结果数组,缓存行和所有内容可能不是一个好主意。更新局部变量,并在计算完成后执行单个存储。
完全忽略了你正在使用奔腾4.据我所知,没有P4的多核版本 - 为了给出多核的幻觉,它有Hyper-Threading:两个逻辑核心共享一个物理核心的执行单元。如果您的线程依赖于相同的执行单元,那么您的性能将与(或更差!)单线程性能相同。例如,您需要在一个线程中进行浮点计算,在另一个线程中进行整数计算以获得性能改进。
P4 HT实现受到了很多批评,较新的实现(最近的core2)应该更好。
答案 1 :(得分:4)
尝试稍微增加数组的大小。不,真的。
在同一个线程中按顺序分配的小对象最初将顺序分配。这可能在同一个缓存行中。如果你有两个内核访问同一个缓存行(然后micro-benhcmark基本上只是对同一个地址执行一系列写操作),那么他们将不得不争取访问。
java.util.concurrent
中有一个类有一堆未使用的long
字段。它们的目的是将不同线程经常使用的对象分成不同的缓存行。
答案 2 :(得分:2)
我对这种差异并不感到惊讶。您正在使用Java的并发框架来创建线程(尽管我没有看到任何保证甚至创建了两个线程,因为第一个作业可能在第二个作业完成之前完成。
在幕后可能会进行各种各样的锁定和同步,而您实际上并不需要进行简单的测试。简而言之,我做认为问题是多线程的开销。
答案 3 :(得分:1)
你对i没有做任何事情,所以你的循环可能只是最优化了。
答案 4 :(得分:1)
您是否使用Runtime.getRuntime()检查了PC上的可用内核数量.availableProcessors()?
答案 5 :(得分:0)
您的代码只是增加一个变量 - 无论如何这是一个非常快速的操作。你在这里使用多个线程并没有获得太多收益。当线程1必须等待某些外部响应或执行一些更复杂的计算时,性能增益更加明显,同时主线程或其他线程可以继续处理并且不会等待。如果您计算更高或使用更多线程(可能是一个安全数字是您机器中的CPU /核心数),您可能会获得更多收益。