我正在学习多线程并发现在多线程环境中减慢了Object.hashCode的速度,因为计算运行4个线程的默认哈希码与相同数量的对象的1个线程相比,它需要花费两倍的时间。
但根据我的理解,并行执行此操作需要花费相同的时间。
您可以更改主题数。每个线程都有相同的工作量,所以你希望在我的四核机器上运行4个线程可能需要与运行单个线程大致相同的时间。
我看到4x只有2.3秒,而1x则看到.9秒。
我的理解是否有任何差距,请帮助我理解这种行为。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
public class ObjectHashCodePerformance {
private static final int THREAD_COUNT = 4;
private static final int ITERATIONS = 20000000;
public static void main(final String[] args) throws Exception {
long start = System.currentTimeMillis();
new ObjectHashCodePerformance().run();
System.err.println(System.currentTimeMillis() - start);
}
private final ExecutorService _sevice = Executors.newFixedThreadPool(THREAD_COUNT,
new ThreadFactory() {
private final ThreadFactory _delegate = Executors.defaultThreadFactory();
@Override
public Thread newThread(final Runnable r) {
Thread thread = _delegate.newThread(r);
thread.setDaemon(true);
return thread;
}
});
private void run() throws Exception {
Callable<Void> work = new java.util.concurrent.Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < ITERATIONS; i++) {
Object object = new Object();
object.hashCode();
}
return null;
}
};
@SuppressWarnings("unchecked")
Callable<Void>[] allWork = new Callable[THREAD_COUNT];
Arrays.fill(allWork, work);
List<Future<Void>> futures = _sevice.invokeAll(Arrays.asList(allWork));
for (Future<Void> future : futures) {
future.get();
}
}
}
对于线程数4输出
~2.3 seconds
对于线程计数1输出
~.9 seconds
答案 0 :(得分:6)
我已经创建了一个简单的JMH基准来测试各种情况:
@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
public class HashCodeBenchmark {
private final Object object = new Object();
@Benchmark
@Threads(1)
public void singleThread(Blackhole blackhole){
blackhole.consume(object.hashCode());
}
@Benchmark
@Threads(2)
public void twoThreads(Blackhole blackhole){
blackhole.consume(object.hashCode());
}
@Benchmark
@Threads(4)
public void fourThreads(Blackhole blackhole){
blackhole.consume(object.hashCode());
}
@Benchmark
@Threads(8)
public void eightThreads(Blackhole blackhole){
blackhole.consume(object.hashCode());
}
}
结果如下:
Benchmark Mode Cnt Score Error Units
HashCodeBenchmark.eightThreads avgt 10 5.710 ± 0.087 ns/op
HashCodeBenchmark.fourThreads avgt 10 3.603 ± 0.169 ns/op
HashCodeBenchmark.singleThread avgt 10 3.063 ± 0.011 ns/op
HashCodeBenchmark.twoThreads avgt 10 3.067 ± 0.034 ns/op
所以我们可以看到,只要没有线程而不是核心,每个哈希码的时间保持不变。
PS:正如@Tom Cools评论的那样 - 你正在测试分配速度而不是测试中的hashCode()速度。
答案 1 :(得分:1)
见Palamino的评论:
你没有测量hashCode(),你在单线程时测量2000万个对象的实例化,在运行4个线程时测量8000万个对象。将新的Object()逻辑移出Callable中的for循环,然后你将测量hashCode() - Palamino
答案 2 :(得分:0)
我在代码中看到了两个问题:
以下是您可以尝试的修改版本:
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
public class ObjectHashCodePerformance {
private static final int THREAD_COUNT = 1;
private static final int ITERATIONS = 20000;
private final Object object = new Object();
public static void main(final String[] args) throws Exception {
long start = System.currentTimeMillis();
new ObjectHashCodePerformance().run();
System.err.println(System.currentTimeMillis() - start);
}
private final ExecutorService _sevice = Executors.newFixedThreadPool(THREAD_COUNT,
new ThreadFactory() {
private final ThreadFactory _delegate = Executors.defaultThreadFactory();
@Override
public Thread newThread(final Runnable r) {
Thread thread = _delegate.newThread(r);
thread.setDaemon(true);
return thread;
}
});
private void run() throws Exception {
Callable<Void> work = new java.util.concurrent.Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < ITERATIONS/THREAD_COUNT; i++) {
object.hashCode();
}
return null;
}
};
@SuppressWarnings("unchecked")
Callable<Void>[] allWork = new Callable[ITERATIONS];
Arrays.fill(allWork, work);
List<Future<Void>> futures = _sevice.invokeAll(Arrays.asList(allWork));
System.out.println("Futures size : " + futures.size());
for (Future<Void> future : futures) {
future.get();
}
}
}