2015-12-16 28 views
7

Tôi đã học đa luồng và tìm thấy sự chậm trễ của Object.hashCode trong môi trường đa luồng vì nó mất hơn gấp đôi thời gian để tính toán mã băm mặc định chạy 4 luồng và 1 luồng cho cùng một số đối tượng.Bench Mark trong môi trường Multi threaded

Nhưng theo hiểu biết của tôi, sẽ mất một khoảng thời gian tương tự để thực hiện việc này song song.

Bạn có thể thay đổi số lượng chủ đề. Mỗi thread có cùng một số lượng công việc để làm như vậy bạn hy vọng rằng chạy 4 chủ đề trên một máy tính của tôi mà là máy lõi tứ có thể mất khoảng thời gian tương tự như chạy một chủ đề duy nhất.

Tôi thấy ~ 2,3 giây cho 4x nhưng .9 s cho 1x.

Có bất kỳ khoảng trống nào trong hiểu biết của tôi không, hãy giúp tôi hiểu hành vi này.

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

} 

Đối với số chủ đề 4 Output là

~2.3 seconds 

Đối đếm thread 1 Output là

~.9 seconds 
+0

Xin hãy chia sẻ những thay đổi bạn đã thực hiện từ 1 đến 4 đề – Jan

+0

Các đo lường thời gian không nhất thiết phải cho bạn biết nhiều ở đây. Xem http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Marco13

+1

Có lẽ bạn không đo lường điều đúng: GC, tạo ra những người thi hành và của các chủ đề của nó, phối hợp luồng, instantiations đối tượng, phân bổ bộ nhớ, v.v.Dù sao, các beanchmark là khá vô dụng, vì bạn sẽ không thể thay đổi bất cứ điều gì để thực hiện hashCode() của Object anyway. –

Trả lời

6

tôi đã tạo ra một chuẩn mực JMH đơn giản để kiểm tra các trường hợp khác nhau:

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

Và kết quả như sau:

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 

vì vậy, chúng ta có thể thấy rằng miễn là không có chủ đề hơn lõi, thời gian cho mỗi hashcode vẫn giữ nguyên.

PS: Như @Tom Cools đã nhận xét - bạn đang đo tốc độ phân bổ chứ không phải tốc độ hashCode() trong thử nghiệm của bạn.

+0

Cảm ơn bạn đã phân tích ... :) –

+0

Bạn có thể vui lòng nói ..về công cụ u được sử dụng cho băng ghế dự bị đánh dấu –

+1

nó được gọi là JMH: http://openjdk.java.net/projects/code-tools/jmh/ –

1

Xem Palamino của bình luận:

Bạn không đo hashCode(), bạn đang đo đạc sự khởi tạo của 20 triệu đối tượng khi đơn luồng, và 80 triệu đối tượng khi chạy 4 chủ đề. Di chuyển logic new Object() ra khỏi vòng lặp for trong Callable của bạn, sau đó bạn sẽ được đo hashCode() - Palamino

+2

Ông nói rằng bạn có thể thay đổi số đếm để quan sát vấn đề mà ông mô tả – Marco13

+0

Tôi đã chuyển nó ra cùng một kết quả .. :( –

0

Hai vấn đề tôi thấy với các mã:

  1. Kích thước của allWork [] array bằng lặp.
  2. Và trong khi lặp lại, trong phương thức call() đảm bảo rằng mỗi luồng nhận được phần tải của nó. ITERATIONS/THREAD_COUNT.

Dưới đây là phiên bản sửa đổi, bạn có thể thử:

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

trong phương thức 'run()/call()' bạn vẫn đang phân bổ các đối tượng - vì vậy bạn đang đo hashcode cộng với tốc độ phân bổ. Câu trả lời của bạn là thiếu sót. –

+1

@SvetlinZarev Điểm lấy và cập nhật mã. –

Các vấn đề liên quan