2015-09-08 13 views
7

Tôi đang cố gắng tính tổng các bình phương của các giá trị trong danh sách. Dưới đây là ba biến thể mà tất cả tính toán giá trị yêu cầu. Tôi muốn biết cái nào hiệu quả nhất. Tôi hy vọng chiếc thứ ba sẽ hiệu quả hơn khi tự động đấm bốc chỉ được thực hiện một lần.Có lợi thế nào khi gọi bản đồ sau khi mapToInt, khi nào cần có

// sum of squares 
    int sum = list.stream().map(x -> x * x).reduce((x, y) -> x + y).get(); 
    System.out.println("sum of squares: " + sum); 

    sum = list.stream().mapToInt(x -> x * x).sum(); 
    System.out.println("sum of squares: " + sum); 

    sum = list.stream().mapToInt(x -> x).map(x -> x * x).sum(); 
    System.out.println("sum of squares: " + sum); 
+3

Trong khi ở trường hợp cụ thể này, đúp đồ thêm chút, có những trường hợp tách một biến đổi thành nhiều phần sẽ làm cho việc tính toán có thể đọc được nhiều hơn hoặc đơn giản hơn. (Và, khi hoạt động lập bản đồ tăng trọng lượng, chi phí của giai đoạn bổ sung trở nên tương đối ít hơn.) –

Trả lời

8

Khi nghi ngờ, hãy kiểm tra! Sử dụng JMH, tôi nhận được kết quả như sau trên một danh sách 100k yếu tố (trong micro, nhỏ hơn là tốt hơn):

Benchmark      Mode Samples  Score Error Units 
c.a.p.SO32462798.for_loop  avgt  10 119.110 0.921 us/op 
c.a.p.SO32462798.mapToInt  avgt  10 129.702 1.040 us/op 
c.a.p.SO32462798.mapToInt_map avgt  10 129.753 1.516 us/op 
c.a.p.SO32462798.map_reduce  avgt  10 1262.802 12.197 us/op 
c.a.p.SO32462798.summingInt  avgt  10 134.821 1.203 us/op 

Vì vậy, bạn có, từ nhanh đến chậm hơn:

  • for(int i : list) sum += i*i;
  • mapToInt(x -> x * x).sum()mapToInt(x -> x).map(x -> x * x).sum()
  • collect(Collectors.summingInt(x -> x * x))
  • map(x -> x * x).reduce((x, y) -> x + y).get()

Lưu ý rằng các kết quả phụ thuộc rất nhiều vào các tối ưu hóa JIT. Nếu logic trong ánh xạ phức tạp hơn, một số tối ưu có thể không có sẵn (mã dài hơn = ít nội tuyến) trong trường hợp các phiên bản luồng có thể mất nhiều hơn 4-5 lần so với vòng lặp - nhưng nếu logic đó là CPU nặng sự khác biệt sẽ giảm một lần nữa. Việc lập hồ sơ đơn đăng ký thực tế của bạn sẽ cung cấp cho bạn thêm thông tin.


đang Benchmark để tham khảo:

@State(Scope.Benchmark) 
@BenchmarkMode(Mode.AverageTime) 
public class SO32462798 { 

    List<Integer> list; 

    @Setup public void setup() { 
    list = new Random().ints(100_000).boxed().collect(toList()); 
    } 

    @Benchmark public int for_loop() { 
    int sum = 0; 
    for (int i : list) sum += i * i; 
    return sum; 
    } 

    @Benchmark public int summingInt() { 
    return list.stream().collect(Collectors.summingInt(x -> x * x)); 
    } 

    @Benchmark public int mapToInt() { 
    return list.stream().mapToInt(x -> x * x).sum(); 
    } 

    @Benchmark public int mapToInt_map() { 
    return list.stream().mapToInt(x -> x).map(x -> x * x).sum(); 
    } 

    @Benchmark public int map_reduce() { 
    return list.stream().map(x -> x * x).reduce((x, y) -> x + y).get(); 
    } 
} 
+1

Nó sẽ là tốt đẹp để thêm cũng là một thực hiện vòng lặp đồng bằng cũ làm tài liệu tham khảo. Giống như 'int sum = 0; cho (int i: list) sum + = i * i; return sum; '. Một lựa chọn khác là sử dụng 'list.stream(). Collect (Collectors.summingInt (x -> x * x));' –

+1

@TagirValeev 'summingInt' nhanh hơn mapToInt + sum - thú vị. Đối với vòng lặp nằm trong danh mục riêng của nó như mong đợi. – assylias

+1

Btw, sử dụng 'list = new Random(). Ints (100_000) .boxed(). Collect (Collectors.toList());' trong 'setup()' sẽ gọn gàng! –

1

Tôi hy vọng điều thứ hai là nhanh nhất.

Có quyền anh trong cả hai cũng không phải ví dụ thứ ba (nếu danh sách chứa các phần tử đã đóng). Nhưng, có unboxing.

Ví dụ thứ hai của bạn có thể có hai unboxing (một cho mỗi x trong x*x), trong khi thứ ba chỉ unboxing chỉ một lần. Tuy nhiên, unboxing là nhanh và tôi nghĩ rằng nó không có giá trị để tối ưu hóa đó như là một đường ống dài hơn với một cuộc gọi chức năng bổ sung chắc chắn sẽ làm chậm nó xuống.

Sidenote: nói chung, bạn không nên mong đợi Stream s để được nhanh hơn so với lặp lại thường xuyên trên mảng hoặc danh sách. Khi thực hiện các phép tính toán học, ở đó các vấn đề tốc độ (như thế này) tốt hơn là đi theo một cách khác: chỉ cần lặp qua các phần tử. Nếu đầu ra của bạn là một giá trị tổng hợp, sau đó tổng hợp nó, nếu nó là một ánh xạ, sau đó phân bổ một mảng hoặc danh sách mới có cùng kích thước và điền nó với các giá trị được tính toán.

+2

Do hoạt động của câu hỏi đang tính tổng, không có điểm nào trong việc tạo mảng. Hay nói cách khác, trong trường hợp này, không có gì lặp lại thủ công có thể làm tốt hơn hoạt động của luồng ... – Holger

+0

@Holger Bạn nói đúng về bản đồ. Tuy nhiên bạn vẫn có thể lưu 'list.size()' các cuộc gọi hàm với lặp lại thông thường. –

+0

Bạn đang nói về các cuộc gọi chức năng nào? Khi bạn phát trực tuyến, ví dụ: một 'ArrayList', không có lời gọi hàm' size() 'nào cả. – Holger

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