JavaRush /จาวาบล็อก /Random-TH /การดำเนินการแบบขนานกับอาร์เรย์ใน Java 8 - การแปล
billybonce
ระดับ
Москва

การดำเนินการแบบขนานกับอาร์เรย์ใน Java 8 - การแปล

เผยแพร่ในกลุ่ม
การแปลบทความ
//การดำเนินการอาร์เรย์แบบขนานใน Java 8 //โดย Eric Bruno, 25 มีนาคม 2014 //drdobbs.com/jvm/parallel-array-operations-in-java-8/240166287 //Eric Bruno ทำงานในภาคการเงินและบล็อก สำหรับเว็บไซต์ ดร. ด็อบบ์.
Java รุ่นใหม่ช่วยให้โต้ตอบกับอาร์เรย์แบบขนานได้ง่ายขึ้น ส่งผลให้ประสิทธิภาพดีขึ้นอย่างมากด้วยการเขียนโค้ดขั้นต่ำ ตอนนี้ Oracle กำลังเปิดตัว Java SE 8 ซึ่งเป็นก้าวสำคัญในแง่ของภาษา คุณสมบัติที่สำคัญอย่างหนึ่งของรีลีสนี้คือการปรับปรุงการทำงานพร้อมกัน ซึ่งบางส่วนปรากฏในคลาสพื้นฐาน java.util.Arrays มีการเพิ่มวิธีการใหม่ในคลาสนี้ ซึ่งฉันจะอธิบายในบทความนี้ สิ่งเหล่านี้บางส่วนใช้ในคุณสมบัติใหม่อื่นของ JDK8 - แลมบ์ดา แต่มาลงมือทำธุรกิจกันเถอะ
Arrays.paralellSort()
คุณสมบัติหลายอย่างของ ParallelSort ขึ้นอยู่กับอัลกอริธึมการจัดเรียงแบบขนานที่แยกอาร์เรย์ออกเป็นส่วน ๆ ซ้ำ ๆ เรียงลำดับแล้วรวมเข้าด้วยกันใหม่เป็นอาร์เรย์สุดท้ายพร้อมกัน การใช้แทนวิธี Arrays.sort ตามลำดับที่มีอยู่ส่งผลให้ประสิทธิภาพและประสิทธิผลดีขึ้นเมื่อเรียงลำดับอาร์เรย์ขนาดใหญ่ ตัวอย่างเช่น โค้ดด้านล่างใช้ sequential sort() และ Parallel ParallelSort() เพื่อจัดเรียงอาร์เรย์ข้อมูลเดียวกัน public class ParallelSort { public static void main(String[] args) { ParallelSort mySort = new ParallelSort(); int[] src = null; System.out.println("\nSerial sort:"); src = mySort.getData(); mySort.sortIt(src, false); System.out.println("\nParallel sort:"); src = mySort.getData(); mySort.sortIt(src, true); } public void sortIt(int[] src, boolean parallel) { try { System.out.println("--Array size: " + src.length); long start = System.currentTimeMillis(); if ( parallel == true ) { Arrays.parallelSort(src); } else { Arrays.sort(src); } long end = System.currentTimeMillis(); System.out.println( "--Elapsed sort time: " + (end-start)); } catch ( Exception e ) { e.printStackTrace(); } } private int[] getData() { try { File file = new File("src/parallelsort/myimage.png"); BufferedImage image = ImageIO.read(file); int w = image.getWidth(); int h = image.getHeight(); int[] src = image.getRGB(0, 0, w, h, null, 0, w); int[] data = new int[src.length * 20]; for ( int i = 0; i < 20; i++ ) { System.arraycopy( src, 0, data, i*src.length, src.length); } return data; } catch ( Exception e ) { e.printStackTrace(); } return null; } } เพื่อทดสอบ ฉันโหลดข้อมูลดิบจากรูปภาพลงในอาร์เรย์ ซึ่งใช้พื้นที่ 46,083,360 ไบต์ (และของคุณจะขึ้นอยู่กับรูปภาพนั้นด้วย) ที่คุณจะใช้) วิธีการเรียงลำดับตามลำดับใช้เวลาเกือบ 3,000 มิลลิวินาทีในการเรียงลำดับอาเรย์บนแล็ปท็อป 4 คอร์ของฉัน ในขณะที่วิธีการเรียงลำดับแบบขนานใช้เวลามากที่สุดประมาณ 700 มิลลิวินาที เห็นด้วย การอัปเดตภาษาใหม่ไม่ได้ช่วยเพิ่มประสิทธิภาพชั้นเรียนถึง 4 เท่าเสมอไป
Arrays.parallelPrefix()
วิธี ParallelPrefix ใช้ฟังก์ชันทางคณิตศาสตร์ที่ระบุกับองค์ประกอบของอาร์เรย์รวมกัน โดยประมวลผลผลลัพธ์ภายในอาร์เรย์แบบขนาน สิ่งนี้มีประสิทธิภาพมากกว่ามากกับฮาร์ดแวร์แบบมัลติคอร์สมัยใหม่ เมื่อเทียบกับการทำงานตามลำดับบนอาเรย์ขนาดใหญ่ มีการนำวิธีนี้ไปใช้งานหลายวิธีสำหรับการดำเนินการข้อมูลพื้นฐานประเภทต่างๆ (เช่น IntBinaryOperator, DoubleBinaryOperator, LongBinaryOperator และอื่นๆ) รวมถึงสำหรับตัวดำเนินการทางคณิตศาสตร์ประเภทต่างๆ นี่คือตัวอย่างของการซ้อนอาร์เรย์แบบขนานโดยใช้อาร์เรย์ขนาดใหญ่เดียวกันกับตัวอย่างก่อนหน้านี้ ซึ่งเสร็จสิ้นภายในเวลาประมาณ 100 มิลลิวินาทีบนแล็ปท็อป 4 คอร์ของฉัน public class MyIntOperator implements IntBinaryOperator { @Override public int applyAsInt(int left, int right) { return left+right; } } public void accumulate() { int[] src = null; // accumulate test System.out.println("\nParallel prefix:"); src = getData(); IntBinaryOperator op = new ParallelSort.MyIntOperator(); long start = System.currentTimeMillis(); Arrays.parallelPrefix(src, new MyIntOperator()); long end = System.currentTimeMillis(); System.out.println("--Elapsed sort time: " + (end-start)); } ... }
Arrays.parallelSetAll()
เมธอด ParallelSetAll() ใหม่จะสร้างอาร์เรย์และตั้งค่าแต่ละองค์ประกอบอาร์เรย์ให้เป็นค่าตามฟังก์ชันที่สร้างค่าเหล่านั้น โดยใช้ความขนานเพื่อปรับปรุงประสิทธิภาพ วิธีการนี้ใช้ lambdas (เรียกว่า "การปิด" ในภาษาอื่น) (และใช่ นี่เป็นข้อผิดพลาดของผู้เขียน เพราะ lambdas และ closures เป็นสิ่งที่แตกต่างกัน) และซึ่งเป็นอีกหนึ่งฟีเจอร์ใหม่ของ JDK8 ที่เราจะกล่าวถึงในบทความต่อๆ ไป จะเพียงพอแล้วที่จะสังเกตว่าแลมบ์ดาซึ่งมีตัวดำเนินการ -> จดจำไวยากรณ์ได้ง่ายให้ดำเนินการทางด้านขวาหลังจากลูกศรสำหรับองค์ประกอบทั้งหมดที่ส่งผ่านไป ในตัวอย่างโค้ดด้านล่าง การดำเนินการจะดำเนินการกับแต่ละองค์ประกอบในอาร์เรย์ซึ่งจัดทำดัชนีโดย i Array.parallelSetAll() สร้างองค์ประกอบอาร์เรย์ ตัวอย่างเช่น โค้ดต่อไปนี้จะเติมอาร์เรย์ขนาดใหญ่ด้วยค่าจำนวนเต็มแบบสุ่ม: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( new Random().nextInt())); } หากต้องการสร้างตัวสร้างองค์ประกอบอาร์เรย์ที่ซับซ้อนมากขึ้น (เช่น ตัวสร้างค่าตามการอ่านจากเซ็นเซอร์ในโลกแห่งความเป็นจริง) คุณสามารถใช้โค้ดที่คล้ายกับ ต่อไปนี้: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( customGenerator(getNextSensorValue()))); } public int customGenerator(int arg){ return arg + 1; // some fancy formula here... } public int getNextSensorValue() { // Just random for illustration return new Random().nextInt(); } เราจะเริ่มต้นด้วย getNextSensorValue ซึ่งในความเป็นจริงแล้ว ระบบจะขอให้เซ็นเซอร์ (เช่น เทอร์โมมิเตอร์) ส่งกลับค่าปัจจุบัน ต่อไปนี้เป็นตัวอย่าง ค่าสุ่มจะถูกสร้างขึ้น เมธอด customGenerator() ต่อไปนี้สร้างอาร์เรย์ขององค์ประกอบโดยใช้ตรรกะที่เลือกตามกรณีที่คุณเลือก นี่เป็นส่วนเพิ่มเติมเล็กๆ น้อยๆ แต่สำหรับกรณีจริง มันจะเป็นบางอย่างที่ซับซ้อนกว่านี้
Spliterator คืออะไร?
นอกเหนือจากคลาส Arrays ที่ใช้การทำงานพร้อมกันและ lambdas ก็คือ Spliterator ซึ่งใช้ในการวนซ้ำและแยกอาร์เรย์ เอฟเฟกต์ไม่ได้จำกัดอยู่เพียงอาร์เรย์ แต่ยังทำงานได้ดีกับคลาส Collection และช่อง IO อีกด้วย ตัวแยกทำงานโดยการแบ่งอาร์เรย์ออกเป็นส่วนต่างๆ โดยอัตโนมัติ และติดตั้งตัวแยกใหม่เพื่อดำเนินการกับอาร์เรย์ย่อยที่เชื่อมโยงเหล่านี้ ชื่อของมันประกอบด้วย Iterator ซึ่ง "แบ่ง" งานการย้ายการวนซ้ำออกเป็นส่วนๆ การใช้ข้อมูลเดียวกันของเรา เราสามารถดำเนินการแยกส่วนในอาร์เรย์ของเราได้ดังต่อไปนี้: การดำเนินการกับข้อมูลในลักษณะนี้ใช้ประโยชน์จากความเท่าเทียม คุณยังสามารถตั้งค่าพารามิเตอร์ตัวแยก เช่น ขนาดต่ำสุดของแต่ละอาร์เรย์ย่อย public void spliterate() { System.out.println("\nSpliterate:"); int[] src = getData(); Spliterator spliterator = Arrays.spliterator(src); spliterator.forEachRemaining( n -> action(n) ); } public void action(int value) { System.out.println("value:"+value); // Perform some real work on this data here... }
สตรีม - การประมวลผล
สุดท้ายนี้ จาก Array คุณสามารถสร้างออบเจ็กต์ Stream ซึ่งช่วยให้สามารถประมวลผลตัวอย่างข้อมูลโดยรวมแบบขนาน โดยสรุปเป็นลำดับสตรีมได้ ความแตกต่างระหว่างคอลเลคชันข้อมูลและสตรีมจาก JDK8 ใหม่ก็คือคอลเลกชั่นอนุญาตให้คุณทำงานกับองค์ประกอบแยกกันเมื่อสตรีมไม่ได้ทำงาน ตัวอย่างเช่น ในคอลเลกชัน คุณสามารถเพิ่มองค์ประกอบ ลบออก และแทรกไว้ตรงกลางได้ ลำดับสตรีมไม่อนุญาตให้คุณจัดการแต่ละองค์ประกอบจากชุดข้อมูล แต่อนุญาตให้คุณทำหน้าที่กับข้อมูลโดยรวมแทน คุณสามารถดำเนินการที่เป็นประโยชน์ เช่น แยกเฉพาะค่าที่ระบุ (โดยไม่สนใจการซ้ำซ้อน) จากชุด การดำเนินการแปลงข้อมูล การค้นหาค่าต่ำสุดและสูงสุดของอาร์เรย์ ฟังก์ชันลดแผนที่ (ใช้ในการคำนวณแบบกระจาย) และการดำเนินการทางคณิตศาสตร์อื่นๆ ตัวอย่างง่ายๆ ต่อไปนี้ใช้การทำงานพร้อมกันเพื่อประมวลผลอาร์เรย์ของข้อมูลแบบขนานและรวมองค์ประกอบต่างๆ public void streamProcessing() { int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); }
บทสรุป
Java 8 จะเป็นหนึ่งในการอัปเดตที่มีประโยชน์ที่สุดสำหรับภาษาอย่างแน่นอน คุณสมบัติคู่ขนานที่กล่าวถึงในที่นี้ lambdas และส่วนขยายอื่นๆ มากมายจะเป็นหัวข้อของการรีวิว Java 8 อื่นๆ บนเว็บไซต์ของเรา
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION