นี่เป็นอีกเทคนิคหนึ่งที่ฉันวิ่งข้ามวัน:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Collections.nCopiesโทรสร้างListที่มีnสำเนาของสิ่งที่คุณให้คุ้มค่า ในกรณีนี้เป็นIntegerค่าบรรจุกล่อง1 แน่นอนว่ามันไม่ได้สร้างรายการที่มีnองค์ประกอบ จะสร้างรายการ "virtualized" ที่มีเฉพาะค่าและความยาวและการเรียกไปยังgetภายในช่วงจะส่งกลับค่า nCopiesวิธีการได้รับรอบตั้งแต่คอลเลกชันกรอบได้รับการแนะนำทางกลับใน JDK 1.2 แน่นอนความสามารถในการสร้างสตรีมจากผลลัพธ์ได้ถูกเพิ่มใน Java SE 8
เรื่องใหญ่อีกวิธีหนึ่งในการทำสิ่งเดียวกันในจำนวนบรรทัดเดียวกัน
แต่เทคนิคนี้จะเร็วกว่าIntStream.generateและIntStream.iterateวิธีการและที่น่าแปลกใจก็ยังเร็วกว่าIntStream.rangeวิธีการ
สำหรับiterateและgenerateผลลัพธ์อาจไม่น่าแปลกใจเกินไป เฟรมเวิร์กของสตรีม (จริงๆคือ Spliterators สำหรับสตรีมเหล่านี้) สร้างขึ้นจากสมมติฐานที่ว่า lambdas อาจสร้างค่าที่แตกต่างกันในแต่ละครั้งและจะสร้างผลลัพธ์ที่ไม่ถูกผูกมัด ทำให้การแยกขนานยากเป็นพิเศษ iterateวิธีการยังเป็นปัญหาสำหรับกรณีนี้เพราะการโทรแต่ละครั้งต้องมีผลมาจากการอย่างใดอย่างหนึ่งก่อนหน้านี้ ดังนั้นสตรีมที่ใช้generateและiterateทำได้ไม่ดีนักในการสร้างค่าคงที่ซ้ำ ๆ
ประสิทธิภาพที่ค่อนข้างแย่rangeเป็นที่น่าแปลกใจ สิ่งนี้ก็เสมือนจริงเช่นกันดังนั้นองค์ประกอบทั้งหมดจึงไม่มีอยู่ในหน่วยความจำจริง ๆ และขนาดจะถูกรู้ล่วงหน้า สิ่งนี้ควรทำให้ตัวแยกสัญญาณที่รวดเร็วและขนานกันได้ง่าย แต่น่าแปลกใจที่ทำได้ไม่ดีนัก บางทีเหตุผลก็คือrangeต้องคำนวณค่าสำหรับแต่ละองค์ประกอบของช่วงแล้วเรียกใช้ฟังก์ชัน แต่ฟังก์ชั่นนี้ไม่สนใจอินพุตของมันและส่งกลับค่าคงที่ดังนั้นฉันจึงแปลกใจที่นี่ไม่อินไลน์และถูกฆ่า
Collections.nCopiesเทคนิคมีการทำมวย / unboxing Listเพื่อจัดการค่าเนื่องจากไม่มีความเชี่ยวชาญดั้งเดิมของ เนื่องจากค่าเหมือนกันทุกครั้งโดยทั่วไปจะใส่กล่องครั้งเดียวและกล่องนั้นจะแชร์โดยnสำเนาทั้งหมด ฉันสงสัยว่าการชกมวย / การแกะกล่องนั้นได้รับการปรับให้เหมาะสมอย่างมากแม้จะอยู่ภายในและสามารถสอดใส่ได้ดี
นี่คือรหัส:
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();
}
และนี่คือผลลัพธ์ JMH: (2.8GHz Core2Duo)
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
มีความแปรปรวนอยู่พอสมควรในเวอร์ชัน ncopies แต่โดยรวมแล้วดูเหมือนว่าเร็วกว่ารุ่น range ถึง 20 เท่า (ฉันค่อนข้างเต็มใจที่จะเชื่อว่าฉันทำอะไรผิดพลาด)
ฉันประหลาดใจที่nCopiesเทคนิคนี้ทำงานได้ดีเพียงใด ภายในไม่ได้ทำอะไรพิเศษมากนักด้วยสตรีมของรายการเวอร์ชวลไลซ์เพียงแค่ใช้งานIntStream.range! ฉันคาดไว้ว่าจะต้องสร้างตัวแยกเฉพาะเพื่อให้มันทำงานได้เร็ว แต่ดูเหมือนว่าจะดีอยู่แล้ว