2015-12-16 113 views
7

我在学习多线程,发现多线程环境下Object.hashCode的速度变慢,因为它占用了两倍长的时间来计算运行4线程vs 1线程的默认哈希码的物体。多线程环境中的Bench Mark

但根据我的理解,它应该花费相当多的时间来并行处理这个问题。

您可以更改线程数。每个线程都有相同数量的工作,所以希望在我的四核机器上运行4个线程可能需要大约与运行单线程相同的时间。

我看到4x为2.3秒,而1x为0.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

请分享你的1个4线程 – Jan

+0

之间所做的更改时间测量不一定告诉你很多在这里。查看http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Marco13

+1

你可能不是在衡量正确的事情:GC,创建执行者和它的线程,线程协调,对象实例化,内存分配等等。无论如何,beanchmark是非常没用的,因为无论如何你都不能改变Object的hashCode()实现。 –

回答

6

我创建了一个简单的江铃控股的基准测试的各类案件:

@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()速度。

+0

感谢您的分析... :) –

+0

你能告诉..你用的工具基准测试 –

+1

它被称为JMH:http://openjdk.java.net/projects/code-tools/jmh/ –

1

见Palamino的评论:

你不能测量的hashCode()你正在测量单线程时2000万个对象的实例化,以及运行4个时8000万个对象线程。将新的对象()逻辑出的for循环在你的可赎回,那么你将被测量的hashCode() - Palamino

+2

他说,你可以改变线程数,以观察他描述的问题 – Marco13

+0

我把它移出了相同的结果.. :( –

0

两个问题我的代码看到:

  1. allWork []数组等于迭代的大小。
  2. 在迭代时,在call()方法中确保每个线程都获得它的负载份额。 ITERATIONS/THREAD_COUNT。

下面是修改后的版本,你可以试试:

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(); 
    } 
} 

} 
+1

在run()/ call()方法中,你仍然在分配对象 - 所以你测量的是散列码加上分配速度。你的答案是有缺陷的。 –

+1

@SvetlinZarev点采取并更新了代码。 –