为什么两个线程的计算速度比一个慢?
Why are two threads calculating slower than one?
我正在开发使用 Java 制作的基准测试应用程序。我 运行 这个代码在主线程上两次,然后在两个单独的线程中一次:
if (testing == null) {
testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE];
}
for (int x = 0; x < TEST_SIZE; x ++) {
for (int y = 0; y < TEST_SIZE; y ++) {
for (int z = 0; z < TEST_SIZE; z ++) {
testing[x][y][z] = (byte)RANDOM.nextInt(100);
}
}
}
if (finished == Test.LOOP_COUNT - 1) {
testing = null;
}
主线程上的任务完成速度比两个线程快得多,如应用程序的输出所示:
Starting test Array Handling with a single core.
Loop #1 finished in 1.820588011 seconds.
Loop #2 finished in 1.779667175 seconds.
Finished in 3 seconds.
Starting test Array Handling with multiple cores.
Loop #2 finished in 9.433253526 seconds.
Loop #1 finished in 9.465652985 seconds.
Finished in 9 seconds.
我在某处读到一些东西说两个线程 运行 真正快速的操作不会像单个线程那样执行,但是两个线程处理更苛刻的操作优于单个线程。我不认为是这种情况,因为每个循环都要求很高。我能想到的唯一原因是线程实际上并没有 运行 在它们自己的内核上。这可能是问题所在吗?我有一个 2 核 4 线程英特尔酷睿 i7-3537U。
编辑:
测试class:
package net.jibini.park.tests;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @author zgoethel12
*/
public abstract class Test {
public static final Random RANDOM = new Random();
public static final int LOOP_COUNT = 2;
public static final CopyOnWriteArrayList<Test> tests = new CopyOnWriteArrayList<Test>();
public int finished = 0;
public int longestTime = 0;
public double timeSum = 0;
static {
RANDOM.setSeed(481923);
tests.add(new TestArray());
}
public abstract String getName();
public void runTest(final boolean multithread) {
new Thread(new Runnable() {
@Override
public void run() {
finished = 0;
longestTime = 0;
timeSum = 0;
if (multithread) {
for (int i = 0; i < LOOP_COUNT; i ++) {
final int f = i;
new Thread(new Runnable() {
@Override
public void run() {
doLoop(f + 1);
Thread.currentThread().interrupt();
}
}).start();
}
} else {
for (int i = 0; i < LOOP_COUNT; i ++) {
doLoop(i + 1);
}
}
while (finished < LOOP_COUNT) {
System.out.print("");
}
System.out.println("Finished in " + (multithread ? longestTime : (int)timeSum) + " seconds.");
Thread.currentThread().interrupt();
}
}).start();
}
public void doLoop(int id) {
long start = System.nanoTime();
doTest(id);
handleLoopFinish(id, start);
}
public abstract void doTest(int id);
public void handleLoopFinish(int id, long start) {
long current = System.nanoTime();
long difference = current - start;
double seconds = (double)difference / 1000000000;
if (seconds > longestTime) {
longestTime = (int)seconds;
}
timeSum += seconds;
System.out.println("Loop #" + id + " finished in " + seconds + " seconds.");
finished ++;
}
}
数组测试:
package net.jibini.park.tests;
/**
*
* @author zgoethel12
*/
public class TestArray extends Test {
public static final int TEST_SIZE = 512;
byte[][][] testing = null;
@Override
public void doTest(int id) {
if (testing == null) {
testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE];
}
for (int x = 0; x < TEST_SIZE; x ++) {
for (int y = 0; y < TEST_SIZE; y ++) {
for (int z = 0; z < TEST_SIZE; z ++) {
testing[x][y][z] = (byte)RANDOM.nextInt(100);
}
}
}
if (finished == Test.LOOP_COUNT - 1) {
testing = null;
}
}
@Override
public String getName() {
return "Array Handling";
}
}
启动第二个线程,这取决于您 select 执行多线程的库,需要大量开销,以及您似乎正在执行的操作,具体取决于大小 TEST_SIZE 很可能由一个 CPU 非常有效地执行,因为您以连续的方式遍历内存中的数组。
我在课程作业中了解到,内置的 Thread Java 库比 ForkJoin 框架的开销要大得多,因此使用该库可能会获得更多预期结果。
作为资源,这是我在并发中使用的一个不错的网站 class:
http://homes.cs.washington.edu/~djg/teachingMaterials/spac/grossmanSPAC_forkJoinFramework.html
小心做热身跑,如下所述。它们对于确保您可以看到好处至关重要。我们尝试进行约 100 次跑步,其中约 10 次作为热身,以确保您获得良好的平均成绩。由于上下文 switching/other 计算机进程可能会增加您的试验的可变性,因此对多次运行进行平均有很大帮助!
您似乎只使用了一个 RANDOM
对象。恐怕这是在两个线程之间共享的,这会使它们变得非常慢。
尝试使用ThreadLocalRandom
.
我正在开发使用 Java 制作的基准测试应用程序。我 运行 这个代码在主线程上两次,然后在两个单独的线程中一次:
if (testing == null) {
testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE];
}
for (int x = 0; x < TEST_SIZE; x ++) {
for (int y = 0; y < TEST_SIZE; y ++) {
for (int z = 0; z < TEST_SIZE; z ++) {
testing[x][y][z] = (byte)RANDOM.nextInt(100);
}
}
}
if (finished == Test.LOOP_COUNT - 1) {
testing = null;
}
主线程上的任务完成速度比两个线程快得多,如应用程序的输出所示:
Starting test Array Handling with a single core.
Loop #1 finished in 1.820588011 seconds.
Loop #2 finished in 1.779667175 seconds.
Finished in 3 seconds.
Starting test Array Handling with multiple cores.
Loop #2 finished in 9.433253526 seconds.
Loop #1 finished in 9.465652985 seconds.
Finished in 9 seconds.
我在某处读到一些东西说两个线程 运行 真正快速的操作不会像单个线程那样执行,但是两个线程处理更苛刻的操作优于单个线程。我不认为是这种情况,因为每个循环都要求很高。我能想到的唯一原因是线程实际上并没有 运行 在它们自己的内核上。这可能是问题所在吗?我有一个 2 核 4 线程英特尔酷睿 i7-3537U。
编辑:
测试class:
package net.jibini.park.tests;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @author zgoethel12
*/
public abstract class Test {
public static final Random RANDOM = new Random();
public static final int LOOP_COUNT = 2;
public static final CopyOnWriteArrayList<Test> tests = new CopyOnWriteArrayList<Test>();
public int finished = 0;
public int longestTime = 0;
public double timeSum = 0;
static {
RANDOM.setSeed(481923);
tests.add(new TestArray());
}
public abstract String getName();
public void runTest(final boolean multithread) {
new Thread(new Runnable() {
@Override
public void run() {
finished = 0;
longestTime = 0;
timeSum = 0;
if (multithread) {
for (int i = 0; i < LOOP_COUNT; i ++) {
final int f = i;
new Thread(new Runnable() {
@Override
public void run() {
doLoop(f + 1);
Thread.currentThread().interrupt();
}
}).start();
}
} else {
for (int i = 0; i < LOOP_COUNT; i ++) {
doLoop(i + 1);
}
}
while (finished < LOOP_COUNT) {
System.out.print("");
}
System.out.println("Finished in " + (multithread ? longestTime : (int)timeSum) + " seconds.");
Thread.currentThread().interrupt();
}
}).start();
}
public void doLoop(int id) {
long start = System.nanoTime();
doTest(id);
handleLoopFinish(id, start);
}
public abstract void doTest(int id);
public void handleLoopFinish(int id, long start) {
long current = System.nanoTime();
long difference = current - start;
double seconds = (double)difference / 1000000000;
if (seconds > longestTime) {
longestTime = (int)seconds;
}
timeSum += seconds;
System.out.println("Loop #" + id + " finished in " + seconds + " seconds.");
finished ++;
}
}
数组测试:
package net.jibini.park.tests;
/**
*
* @author zgoethel12
*/
public class TestArray extends Test {
public static final int TEST_SIZE = 512;
byte[][][] testing = null;
@Override
public void doTest(int id) {
if (testing == null) {
testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE];
}
for (int x = 0; x < TEST_SIZE; x ++) {
for (int y = 0; y < TEST_SIZE; y ++) {
for (int z = 0; z < TEST_SIZE; z ++) {
testing[x][y][z] = (byte)RANDOM.nextInt(100);
}
}
}
if (finished == Test.LOOP_COUNT - 1) {
testing = null;
}
}
@Override
public String getName() {
return "Array Handling";
}
}
启动第二个线程,这取决于您 select 执行多线程的库,需要大量开销,以及您似乎正在执行的操作,具体取决于大小 TEST_SIZE 很可能由一个 CPU 非常有效地执行,因为您以连续的方式遍历内存中的数组。
我在课程作业中了解到,内置的 Thread Java 库比 ForkJoin 框架的开销要大得多,因此使用该库可能会获得更多预期结果。
作为资源,这是我在并发中使用的一个不错的网站 class: http://homes.cs.washington.edu/~djg/teachingMaterials/spac/grossmanSPAC_forkJoinFramework.html
小心做热身跑,如下所述。它们对于确保您可以看到好处至关重要。我们尝试进行约 100 次跑步,其中约 10 次作为热身,以确保您获得良好的平均成绩。由于上下文 switching/other 计算机进程可能会增加您的试验的可变性,因此对多次运行进行平均有很大帮助!
您似乎只使用了一个 RANDOM
对象。恐怕这是在两个线程之间共享的,这会使它们变得非常慢。
尝试使用ThreadLocalRandom
.