Đây là một kỹ thuật khác mà tôi đã thử nghiệm vào ngày hôm trước:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Cuộc Collections.nCopies
gọi tạo ra một bản sao List
có chứa n
bất kỳ giá trị nào bạn cung cấp. Trong trường hợp này, đó là Integer
giá trị được đóng hộp 1. Tất nhiên nó không thực sự tạo một danh sách với n
các phần tử; nó tạo ra một danh sách "ảo hóa" chỉ chứa giá trị và độ dài, và bất kỳ lệnh gọi nào đến get
trong phạm vi đều chỉ trả về giá trị. Các nCopies
phương pháp đã được khoảng từ Framework Các bộ sưu tập được giới thiệu cách trở lại trong JDK 1.2. Tất nhiên, khả năng tạo luồng từ kết quả của nó đã được thêm vào trong Java SE 8.
Việc lớn, một cách khác để làm điều tương tự với cùng một số dòng.
Tuy nhiên, kỹ thuật này nhanh hơn phương pháp IntStream.generate
và IntStream.iterate
phương pháp tiếp cận, và đáng ngạc nhiên là nó cũng nhanh hơn IntStream.range
phương pháp tiếp cận.
Đối với iterate
và generate
kết quả có lẽ không quá ngạc nhiên. Khung luồng (thực sự là Spliterator cho các luồng này) được xây dựng dựa trên giả định rằng các lambdas sẽ có khả năng tạo ra các giá trị khác nhau mỗi lần và chúng sẽ tạo ra một số lượng kết quả không giới hạn. Điều này làm cho việc tách song song trở nên đặc biệt khó khăn. Các iterate
phương pháp cũng là vấn đề đối với trường hợp này bởi vì mỗi cuộc gọi đòi hỏi kết quả của tuần trước. Vì vậy, các luồng sử dụng generate
và iterate
không hoạt động rất tốt trong việc tạo các hằng số lặp lại.
Hiệu suất tương đối kém của range
là đáng ngạc nhiên. Điều này cũng được ảo hóa, vì vậy các phần tử không thực sự tồn tại trong bộ nhớ và kích thước được biết trước. Điều này sẽ tạo ra một bộ tách sóng song song nhanh và dễ dàng. Nhưng đáng ngạc nhiên là nó không hoạt động tốt lắm. Có lẽ lý do là range
phải tính toán một giá trị cho mỗi phần tử của phạm vi và sau đó gọi một hàm trên đó. Nhưng hàm này chỉ bỏ qua đầu vào của nó và trả về một hằng số, vì vậy tôi ngạc nhiên vì điều này không được nội tuyến và bị giết.
Các Collections.nCopies
kỹ thuật đã làm boxing / unboxing để xử lý các giá trị, vì không có chuyên ngành nguyên thủy của List
. Vì giá trị của mỗi lần như nhau , về cơ bản nó được đóng hộp một lần và hộp đó được chia sẻ bởi tất cả các n
bản sao. Tôi nghi ngờ quyền anh / unboxing được tối ưu hóa rất cao, thậm chí còn hấp dẫn, và nó có thể được sắp xếp tốt.
Đây là mã:
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
Và đây là kết quả JMH: (Core2Duo 2,8GHz)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
Có một số lượng lớn sự khác biệt trong phiên bản ncopies, nhưng nhìn chung, nó có vẻ nhanh hơn 20 lần so với phiên bản phạm vi. (Tuy nhiên, tôi khá sẵn lòng tin rằng tôi đã làm sai điều gì đó.)
Tôi ngạc nhiên về cách nCopies
hoạt động của kỹ thuật này. Bên trong nó không có gì đặc biệt lắm, với luồng danh sách ảo hóa được triển khai đơn giản bằng cách sử dụng IntStream.range
! Tôi đã nghĩ rằng cần phải tạo một trình phân tách chuyên biệt để làm cho việc này diễn ra nhanh chóng, nhưng có vẻ như nó đã khá ổn.