2012-08-03 23 views
7

Tôi có mã tiêu thụ một số lượng lớn (hàng triệu hiện tại, cuối cùng là hàng tỷ) các mảng ngẫu nhiên tương đối ngắn (5-100 phần tử) và thực hiện một số phép toán không quá vất vả chúng. Các số ngẫu nhiên, tốt, ngẫu nhiên, lý tưởng tôi muốn tạo chúng trên nhiều lõi, vì việc tạo số ngẫu nhiên là> 50% thời gian chạy của tôi trong lược tả. Tuy nhiên, tôi gặp khó khăn trong việc phân phối một số lượng lớn các tác vụ nhỏ theo cách không chậm hơn so với phương pháp đơn luồng.Hiệu suất cao đệm cho một dòng các dải ngang

Mã của tôi hiện trông giống như sau:

for(int i=0;i<1000000;i++){ 
    for(RealVector d:data){ 
     while(!converged){ 
      double[] shortVec = new double[5]; 
      for(int i=0;i<5;i++) shortVec[i]=rng.nextGaussian(); 
      double[] longerVec = new double[50]; 
      for(int i=0;i<50;i++) longerVec[i]=rng.nextGaussian(); 
      /*Do some relatively fast math*/ 
     } 
    } 
} 

phương pháp tiếp cận tôi đã lấy có không làm việc là:

  • 1+ đề Populating một ArrayBlockingQueue, và tốn nhiều vòng lặp chính của tôi và điền vào các mảng (các boxing/unboxing là kẻ giết người ở đây)
  • Tạo các vectơ với một Callable (năng suất một tương lai) trong khi làm các phần không phụ thuộc của toán học (nó xuất hiện trên đầu của bất đẳng thức lớn hơn bất kỳ lợi ích song song nào tôi nhận được)
  • Sử dụng 2 ArrayBlockingQueue, mỗi chuỗi được điền bởi một chuỗi, một cho ngắn và một cho mảng dài (vẫn còn gần gấp đôi so với luồng đơn trực tiếp trường hợp).

Tôi không tìm kiếm "giải pháp" cho vấn đề cụ thể của mình nhiều như cách xử lý trường hợp chung tạo ra dòng lớn nhỏ, độc lập nguyên thủy song song và tiêu thụ chúng từ một chuỗi đơn.

Trả lời

4

Đây là hiệu quả hơn so với sử dụng một Queue vì;

  • tải trọng là một mảng double[] có nghĩa là chủ đề nền có thể tạo thêm dữ liệu trước khi phải vượt qua.
  • tất cả các đối tượng được tái chế.

.

public class RandomGenerator { 
    private final ExecutorService generator = Executors.newSingleThreadExecutor(new ThreadFactory() { 
     @Override 
     public Thread newThread(Runnable r) { 
      Thread t = new Thread(r, "generator"); 
      t.setDaemon(true); 
      return t; 
     } 
    }); 
    private final Exchanger<double[][]> exchanger = new Exchanger<>(); 
    private double[][] buffer; 
    private int nextRow = Integer.MAX_VALUE; 

    public RandomGenerator(final int rows, final int columns) { 
     buffer = new double[rows][columns]; 
     generator.submit(new Callable<Void>() { 
      @Override 
      public Void call() throws Exception { 
       Random random = new Random(); 
       double[][] buffer2 = new double[rows][columns]; 
       while (!Thread.interrupted()) { 
        for (int r = 0; r < rows; r++) 
         for (int c = 0; c < columns; c++) 
          buffer2[r][c] = random.nextGaussian(); 
        buffer2 = exchanger.exchange(buffer2); 
       } 
       return null; 
      } 
     }); 
    } 

    public double[] nextArray() throws InterruptedException { 
     if (nextRow >= buffer.length) { 
      buffer = exchanger.exchange(buffer); 
      nextRow = 0; 
     } 
     return buffer[nextRow++]; 
    } 
} 

ngẫu nhiên là chủ đề an toàn và đồng bộ. Điều này có nghĩa là mỗi thread cần nó ngẫu nhiên để thực hiện đồng thời.

cách xử lý trường hợp chung tạo luồng lớn các nguyên thủy độc lập nhỏ song song và tiêu thụ chúng từ một chuỗi đơn lẻ.

Tôi sẽ sử dụng một Exchanger<double[][]> để cư giá trị trong nền như vượt qua chúng một cách hiệu quả (không có nhiều overhead GC)

+0

@Gray quá đúng. Đến gần hơn để trả lời câu hỏi. –

+0

Đã thêm ví dụ về cách sử dụng Bộ trao đổi để tạo số ngẫu nhiên theo lô trong nền. –

+1

Một sự kết hợp của một bộ trao đổi để giao tiếp và chunking dòng suối dài hơn đã làm rất nhiều cho hiệu suất. Cảm ơn. – Bryce

5

Vấn đề với hiệu suất của bạn dường như là các công việc cá nhân quá nhỏ nên hầu hết thời gian được dành để thực hiện đồng bộ hóa và xếp hàng công việc. Một điều cần xem xét là không phải để tạo một luồng lớn các công việc nhỏ nhưng để phân phối tới từng chuỗi hoạt động, một bộ sưu tập có kích thước trung bình mà công việc sẽ chú thích với câu trả lời. Ví dụ, thay vì lặp qua vòng lặp của bạn với chuỗi đầu tiên làm lặp lại # 0, chuỗi tiếp theo đang lặp lại # 1, ... Tôi sẽ có chuỗi đầu tiên lặp lại từ # 0 đến # 999 hoặc một số ví dụ như vậy. Họ phải làm việc độc lập và chú thích một lớp học Job với câu trả lời về tính toán của họ. Sau đó, vào cuối, họ có thể trả lại toàn bộ bộ sưu tập các công việc đã được hoàn thành dưới dạng Future.

lớp Job của bạn có thể giống như sau:

public class Job { 
    Collection<RealVector> dataCollection; 
    Collection<SomeAnswer> answerCollection = new ArrayList<SomeAnswer>(); 
    public void run() { 
     for (RealVector d : dataCollection) { 
      // do the magic work on the vector 
      while(!converged){ 
       ... 
      } 
      // put the associated "answer" in another collection 
      answerCollection.add(someAnswer); 
     } 
    } 
} 
+0

chunking dọc theo những đường dường như giống như nó sẽ tránh được một số vấn đề chi phí. Tôi đã hy vọng tránh đi quá nhiều trên con đường đó, bởi vì số lượng vectơ cần thiết cho vòng lặp bên trong thực sự là không xác định, có nghĩa là cần phải có cơ chế cho công việc yêu cầu hoặc tạo ra một chunk-o-rands khác nó chạy ra ngoài, và một số tiền được tạo ra trước đó có thể sẽ bị lãng phí. – Bryce

+0

Một lần nữa, mục tiêu ở đây là giảm thiểu số lượng đồng bộ hóa @Bryce. Có lẽ mỗi thread có thể có một 'ThreadLocal' với các giá trị của nó sao cho không cái nào bị lãng phí? Hoặc chỉ cần tăng kích thước chunk để giảm lãng phí rands cho mỗi công việc cho đến một điểm mà nó không quan trọng trong quá trình chạy. – Gray

+0

Tôi phải sao lưu @Gray trên 5-100 yếu tố này là vô vọng nhỏ cho hiệu quả cho comms liên thread. Nếu bạn có thể nâng cao điều này bằng, giả sử, hệ số 1000, mọi thứ sẽ cải thiện. –

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