2016-12-11 11 views
5

Tôi muốn tìm hiểu lập trình song song để tăng tốc các thuật toán và chọn Java.
Tôi đã viết hai hàm để tổng hợp long số nguyên trong mảng - một đơn giản lặp qua mảng, mảng thứ hai - chia thành các phần và tổng hợp các phần trong các chuỗi được phân tách.Nhiều chủ đề Java mang lại lợi nhuận rất nhỏ

Tôi dự kiến ​​sẽ hợp lý tốc độ gấp 2 lần khi sử dụng hai chuỗi. Tuy nhiên, những gì tôi có chỉ có 24% tăng tốc. Hơn nữa, bằng cách sử dụng nhiều chủ đề, tôi không nhận được bất kỳ cải tiến (có thể ít hơn 1%) trên hai chủ đề. Tôi biết rằng nên có thread tạo/tham gia trên cao, nhưng tôi đoán nó không phải là lớn.

Bạn có thể giải thích, những gì tôi bị thiếu hoặc có lỗi trong mã không? Đây là mã:

import java.util.concurrent.ThreadLocalRandom; 


public class ParallelTest { 


public static long sum1 (long[] num, int a, int b) { 
    long r = 0; 
    while (a < b) { 
     r += num[a]; 
     ++a; 
    } 
    return r; 
} 

public static class SumThread extends Thread { 
    private long num[]; 
    private long r; 
    private int a, b; 

    public SumThread (long[] num, int a, int b) { 
     super(); 
     this.num = num; 
     this.a = a; 
     this.b = b; 
    } 

    @Override 
    public void run() { 
     r = ParallelTest.sum1(num, a, b); 
    } 

    public long getSum() { 
     return r; 
    } 
} 


public static long sum2 (long[] num, int a, int b, int threadCnt) throws InterruptedException { 
    SumThread[] th = new SumThread[threadCnt]; 
    int i = 0, c = (b - a + threadCnt - 1)/threadCnt; 

    for (;;) { 
     int a2 = a + c; 
     if (a2 > b) { 
      a2 = b; 
     } 
     th[i] = new SumThread(num, a, a2); 
     th[i].start(); 
     if (a2 == b) { 
      break; 
     } 
     a = a2; 
     ++i; 
    } 

    for (i = 0; i < threadCnt; ++i) { 
     th[i].join(); 
    } 
    long r = 0; 
    for (i = 0; i < threadCnt; ++i) { 
     r += th[i].getSum(); 
    } 
    return r; 
} 

public static void main(String[] args) throws InterruptedException { 
    final int N = 230000000; 
    long[] num = new long[N]; 

    for (int i = 0; i < N; ++i) { 
     num[i] = ThreadLocalRandom.current().nextLong(1, 9999); 
    } 

    // System.out.println(Runtime.getRuntime().availableProcessors()); 

    long timestamp = System.nanoTime(); 
    System.out.println(sum1(num, 0, num.length)); 
    System.out.println(System.nanoTime() - timestamp); 

    for (int n = 2; n <= 4; ++n) { 
     timestamp = System.nanoTime(); 
     System.out.println(sum2(num, 0, num.length, n)); 
     System.out.println(System.nanoTime() - timestamp); 
    } 


} 
} 

CHỈNH SỬA: Tôi có bộ vi xử lý i7 với 4 lõi (8 chủ đề). Output do mã này là:

1149914787860 
175689196 
1149914787860 
149224086 
1149914787860 
147709988 
1149914787860 
138243999 

Trả lời

3

Chương trình có lẽ là băng thông bộ nhớ chính bị giới hạn chỉ với hai chủ đề, vì đó là một vòng lặp nhỏ, tìm nạp dữ liệu nhanh như ram có thể cung cấp dữ liệu cho bộ xử lý.

+0

Điều đó có nghĩa là, nếu tôi có nhiều công việc đòi hỏi nhiều CPU hơn trong vòng lặp, thì tôi sẽ có được hiệu suất tốt hơn với nhiều chủ đề hơn? – Somnium

+0

@Somnium - đúng. – rcgldr

3

tôi có thể nghĩ ra một lý do tại sao số bạn có thể không nhận được càng nhiều tăng tốc như bạn đang mong đợi.

  1. Chi phí tạo chủ đề là đáng kể. Chủ đề start() là một hoạt động tốn kém, đòi hỏi nhiều syscalls phân bổ một ngăn xếp luồng và "vùng màu đỏ" của nó và sau đó tạo chuỗi gốc.

  2. Chủ đề N sẽ không bắt đầu cùng một lúc. Điều đó có nghĩa là thời gian hoàn thành phần song song của phép tính sẽ xấp xỉ thời gian kết thúc của luồng cuối cùng - thời gian bắt đầu của lần đầu tiên. Điều đó sẽ lâu hơn thời gian cho một chủ đề cần thực hiện phần công việc của nó. (Bởi N-1 lần thời gian tạo luồng ...)

  3. Chủ đề N (về cơ bản) thực hiện quét tuần tự các phần tách rời của mảng. Đây là băng thông bộ nhớ chuyên sâu, và cách mà bạn đang quét có nghĩa là bộ nhớ cache sẽ không hiệu quả. Do đó, có một cơ hội tốt là hiệu suất bị giới hạn bởi tốc độ và băng thông của phần cứng bộ nhớ chính của hệ thống.

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