ฉันกระตือรือร้นที่จะดู Scala และมีคำถามพื้นฐานที่ฉันไม่สามารถหาคำตอบ: โดยทั่วไปมีประสิทธิภาพและการใช้หน่วยความจำระหว่าง Scala และ Java แตกต่างกันหรือไม่
ฉันกระตือรือร้นที่จะดู Scala และมีคำถามพื้นฐานที่ฉันไม่สามารถหาคำตอบ: โดยทั่วไปมีประสิทธิภาพและการใช้หน่วยความจำระหว่าง Scala และ Java แตกต่างกันหรือไม่
คำตอบ:
สกาล่าทำให้การใช้หน่วยความจำจำนวนมหาศาลนั้นง่ายมากโดยที่ไม่รู้ตัว ซึ่งมักจะมีประสิทธิภาพมาก แต่บางครั้งก็น่ารำคาญ ตัวอย่างเช่นสมมติว่าคุณมีอาร์เรย์ของสตริง (เรียกว่าarray
) และแผนที่จากสตริงเหล่านั้นไปยังไฟล์ (เรียกว่าmapping
) สมมติว่าคุณต้องการรับไฟล์ทั้งหมดที่อยู่ในแผนที่และมาจากสตริงที่มีความยาวมากกว่าสองไฟล์ ใน Java คุณอาจ
int n = 0;
for (String s: array) {
if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
if (s.length <= 2) continue;
bigEnough[n++] = map.get(s);
}
ต๊าย! การทำงานอย่างหนัก. ใน Scala วิธีที่กะทัดรัดที่สุดในการทำสิ่งเดียวกันคือ:
val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)
ง่าย! แต่ถ้าคุณไม่คุ้นเคยกับวิธีการทำงานของคอลเลกชันสิ่งที่คุณอาจไม่ทราบก็คือวิธีการทำเช่นนี้สร้างอาร์เรย์ระดับกลางพิเศษ (พร้อมfilter
) และวัตถุพิเศษสำหรับทุกองค์ประกอบของอาร์เรย์ (ด้วยmapping.get
ซึ่งส่งกลับ ตัวเลือก). นอกจากนี้ยังสร้างสองฟังก์ชั่นวัตถุ (หนึ่งสำหรับตัวกรองและอีกหนึ่งสำหรับ flatMap) แม้ว่ามันจะไม่ค่อยเป็นปัญหาหลักเนื่องจากฟังก์ชั่นวัตถุมีขนาดเล็ก
ดังนั้นโดยทั่วไปการใช้หน่วยความจำอยู่ในระดับดั้งเดิมเหมือนกัน แต่ห้องสมุดของสกาล่ามีวิธีการที่ทรงพลังมากมายที่ให้คุณสร้างวัตถุ (มักมีอายุสั้น) จำนวนมหาศาลได้อย่างง่ายดาย โดยปกติแล้วตัวเก็บขยะค่อนข้างดีกับขยะชนิดนั้น แต่ถ้าคุณลืมสิ่งที่ใช้หน่วยความจำอย่างสมบูรณ์คุณอาจพบปัญหาใน Scala มากกว่า Java
โปรดทราบว่าโค้ดภาษา Scala เกณฑ์มาตรฐานของเกมคอมพิวเตอร์เขียนในรูปแบบที่ค่อนข้างคล้าย Java เพื่อให้ได้ประสิทธิภาพที่เหมือน Java และทำให้มีการใช้หน่วยความจำเหมือน Java คุณสามารถทำได้ใน Scala: ถ้าคุณเขียนโค้ดของคุณให้ดูเหมือนโค้ด Java ประสิทธิภาพสูงมันจะเป็นโค้ดสกาล่าที่มีประสิทธิภาพสูง (คุณอาจจะสามารถเขียนมันในรูปแบบที่เป็นสำนวนสกาล่ามากขึ้นและยังคงได้รับประสิทธิภาพที่ดี แต่ขึ้นอยู่กับเฉพาะ)
ฉันควรเพิ่มจำนวนครั้งที่ใช้ในการเขียนโปรแกรมรหัส Scala ของฉันมักจะเร็วกว่าโค้ด Java ของฉันเนื่องจากใน Scala ฉันสามารถรับส่วนที่ไม่น่าเบื่อที่ไม่สำคัญต่อการปฏิบัติงานได้โดยใช้ความพยายามน้อยลง รหัสสำหรับชิ้นส่วนที่สำคัญต่อประสิทธิภาพ
ฉันเป็นผู้ใช้ใหม่ดังนั้นฉันไม่สามารถเพิ่มความคิดเห็นในคำตอบของ Rex Kerr ด้านบน (การอนุญาตให้ผู้ใช้ใหม่เพื่อ "ตอบ" แต่ไม่ใช่ "ความคิดเห็น" เป็นกฎที่แปลกมาก btw)
ฉันลงทะเบียนเพื่อตอบสนองต่อ "phew, Java นั้นละเอียดมากและการทำงานหนักมาก" ของคำตอบที่ได้รับความนิยมของ Rex ด้านบน ในขณะที่คุณสามารถเขียนโค้ดสกาล่าที่รัดกุมกว่านี้ได้ตัวอย่าง Java ที่ให้นั้นมีความชัดเจน นักพัฒนา Java ส่วนใหญ่จะเขียนโค้ดดังนี้:
List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
if(s.length() > 2 && mapping.get(s) != null) {
bigEnough.add(mapping.get(s));
}
}
และแน่นอนถ้าเราจะแกล้งทำเป็นว่า Eclipse ไม่ได้ทำการพิมพ์จริงสำหรับคุณและตัวละครทุกตัวที่บันทึกไว้จริงๆแล้วทำให้คุณเป็นโปรแกรมเมอร์ที่ดีขึ้นคุณสามารถเขียนโค้ดนี้ได้:
List b=new ArrayList();
for(String s:array)
if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));
ตอนนี้ไม่เพียง แต่ฉันประหยัดเวลาที่ใช้ในการพิมพ์ชื่อตัวแปรแบบเต็มและเครื่องหมายปีกกา (ทำให้ฉันใช้เวลาอีก 5 วินาทีเพื่อคิดความคิดอัลกอริทึมลึก) แต่ฉันยังสามารถป้อนรหัสของฉันในการแข่งขันที่ทำให้งงงวย วันหยุด.
Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
เขียน Scala ของคุณเช่น Java และคุณสามารถคาดหวังว่ารหัสไบต์เดียวกันเกือบจะถูกปล่อยออกมา - ด้วยตัวชี้วัดที่เกือบเหมือนกัน
เขียนมันมากขึ้น "สำนวน" กับวัตถุที่ไม่เปลี่ยนรูปและฟังก์ชั่นการสั่งซื้อที่สูงขึ้นและมันจะช้าลงเล็กน้อยและใหญ่ขึ้นเล็กน้อย ข้อยกเว้นประการหนึ่งสำหรับกฎข้อนี้คือเมื่อใช้ออบเจ็กต์ทั่วไปที่ params ประเภทใช้@specialised
คำอธิบายประกอบสิ่งนี้จะสร้างโค้ดไบต์ขนาดใหญ่ยิ่งขึ้นซึ่งสามารถแซงหน้าประสิทธิภาพของ Java ได้ด้วยการหลีกเลี่ยงการชกมวย / ไม่ทำกล่อง
นอกจากนี้มูลค่าการกล่าวถึงก็คือความจริงที่ว่าหน่วยความจำเพิ่มเติม / ความเร็วที่น้อยลงเป็นการหลีกเลี่ยงไม่ได้เมื่อเขียนโค้ดที่สามารถทำงานแบบขนานได้ รหัส Idiomatic Scala นั้นมีความเป็นธรรมชาติมากกว่ารหัส Java ทั่วไปและมักจะเป็นเพียง 4 ตัวอักษร ( .par
) ห่างจากการขนานกันหมด
ดังนั้นถ้า
คุณจะบอกว่าตอนนี้โค้ดสกาล่านั้นค่อนข้างช้ากว่า 25% หรือเร็วกว่านี้อีกประมาณ 3 เท่า?
คำตอบที่ถูกต้องขึ้นอยู่กับวิธีที่คุณกำหนด "ประสิทธิภาพ" :)
.par
อยู่ใน 2.9
.par
ในกาลาโดยเพียงแค่ใช้
map
วิธีการจะมีขนาดเล็กมาก
เกมเกณฑ์มาตรฐานภาษาคอมพิวเตอร์:
ทดสอบความเร็ว java / สกาล่า 1.71 / 2.25
ทดสอบหน่วยความจำ java / สกาล่า 66.55 / 80.81
ดังนั้นมาตรฐานนี้บอกว่า java เร็วขึ้น 24% และสกาล่าใช้หน่วยความจำเพิ่มขึ้น 21%
ทั้งหมดนั้นไม่ใช่เรื่องใหญ่และไม่ควรสำคัญในแอพในโลกแห่งความเป็นจริงซึ่งฐานข้อมูลและเครือข่ายส่วนใหญ่จะใช้เวลาส่วนใหญ่
บรรทัดล่าง:ถ้าสกาล่าทำให้คุณและทีมงานของคุณ (และคนที่ทำโปรเจ็กต์มากกว่าเมื่อคุณออกจาก) มีประสิทธิผลมากขึ้นคุณควรดำเนินการต่อไป
คนอื่น ๆ ได้ตอบคำถามนี้ด้วยความเคารพต่อลูปที่แน่นแม้ว่าดูเหมือนจะมีความแตกต่างในการทำงานที่ชัดเจนระหว่างตัวอย่างของ Rex Kerr ที่ฉันได้แสดงความคิดเห็นไว้
คำตอบนี้ได้รับการตั้งเป้าหมายไว้ที่ผู้ที่อาจตรวจสอบความต้องการการปรับให้เหมาะสมแบบลูปเป็นข้อบกพร่องในการออกแบบ
ฉันค่อนข้างใหม่กับ Scala (ประมาณหนึ่งปีหรือมากกว่านั้น) แต่ความรู้สึกของมันในตอนนี้ก็คือมันช่วยให้คุณเลื่อนการออกแบบการติดตั้งและการใช้งานได้อย่างง่ายดาย (ด้วยการอ่านพื้นหลังและการทดลอง :)
คุณสมบัติการออกแบบที่เลื่อนออกไป:
คุณสมบัติการใช้งานที่รอการตัดบัญชี:
คุณสมบัติการดำเนินการรอการตัดบัญชี: (ขออภัยไม่มีลิงก์)
สำหรับฉันแล้วฟีเจอร์เหล่านี้เป็นคุณสมบัติที่ช่วยให้เราเหยียบเส้นทางไปสู่แอปพลิเคชันที่รวดเร็วและแน่นหนา
ตัวอย่างของ Rex Kerr นั้นแตกต่างกันไปในด้านของการดำเนินการที่รอการตัดบัญชี ในตัวอย่าง Java การจัดสรรหน่วยความจำจะถูกเลื่อนออกไปจนกว่าจะมีการคำนวณขนาดโดยที่ตัวอย่าง Scala เลื่อนการค้นหาการแมป สำหรับฉันพวกเขาดูเหมือนอัลกอริทึมที่แตกต่างอย่างสิ้นเชิง
นี่คือสิ่งที่ฉันคิดว่าเป็นแอปเปิ้ลมากกว่าแอปเปิ้ลเทียบเท่ากับตัวอย่าง Java ของเขา:
val bigEnough = array.collect({
case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})
ไม่มีคอลเลกชันตัวกลางไม่มีOption
กรณี ฯลฯ นี้ยังเก็บรักษาคอลเลกชันประเภทเพื่อbigEnough
's ประเภทคือArray[File]
- Array
' s collect
การดำเนินการอาจจะทำสิ่งที่ตามเส้นของสิ่งที่นายเคอร์รหัส Java ไม่
คุณลักษณะการออกแบบที่เลื่อนออกไปที่ฉันระบุไว้ด้านบนจะช่วยให้นักพัฒนา API คอลเลกชันของ Scala สามารถใช้งานการรวบรวมเฉพาะอาร์เรย์ที่รวดเร็วในรุ่นอนาคตโดยไม่ต้องทำลาย API นี่คือสิ่งที่ฉันหมายถึงด้วยการเหยียบเส้นทางเพื่อความเร็ว
นอกจากนี้:
val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)
withFilter
วิธีการที่ผมเคยใช้ที่นี่แทนการfilter
แก้ไขปัญหาคอลเลกชันกลาง แต่ยังคงมีปัญหาเช่นตัวเลือก
ตัวอย่างหนึ่งของความเร็วในการใช้งานอย่างง่ายใน Scala คือการบันทึก
ใน Java เราอาจเขียนสิ่งที่ชอบ:
if (logger.isDebugEnabled())
logger.debug("trace");
ในสกาล่านี่เป็นเพียง:
logger.debug("trace")
เนื่องจากพารามิเตอร์ข้อความที่จะดีบั๊กใน Scala มีประเภท " => String
" ซึ่งฉันคิดว่าเป็นฟังก์ชั่นที่ไม่ใช้พารามิเตอร์ที่ดำเนินการเมื่อมีการประเมิน แต่เอกสารที่เรียกใช้ Pass-by-name
แก้ไข {ฟังก์ชั่นใน Scala เป็นวัตถุดังนั้นจึงมีวัตถุพิเศษที่นี่ สำหรับงานของฉันน้ำหนักของวัตถุเล็ก ๆ น้อย ๆ นั้นคุ้มค่าที่จะลบความเป็นไปได้ที่ข้อความบันทึกจะได้รับการประเมินโดยไม่จำเป็น }
สิ่งนี้ไม่ได้ทำให้โค้ดเร็วขึ้น แต่มันจะทำให้มีแนวโน้มที่จะเร็วขึ้นและเรามีโอกาสน้อยที่จะมีประสบการณ์ในการผ่านและทำความสะอาดโค้ดของคนอื่นมากขึ้น
สำหรับฉันนี่เป็นชุดรูปแบบที่สอดคล้องกันภายใน Scala
รหัสยากล้มเหลวในการจับภาพว่าทำไม Scala ถึงเร็วกว่าถึงจะเป็นเพียงเล็กน้อย
ฉันรู้สึกว่ามันเป็นการผสมผสานระหว่างการนำโค้ดกลับมาใช้ใหม่และเพดานคุณภาพของรหัสใน Scala
ใน Java โค้ดที่น่ากลัวมักจะถูกบังคับให้เป็นระเบียบที่เข้าใจไม่ได้ดังนั้นจึงไม่สามารถใช้งานได้จริงใน API คุณภาพการผลิตเนื่องจากโปรแกรมเมอร์ส่วนใหญ่ไม่สามารถใช้งานได้
ฉันมีความหวังสูงว่าสกาล่าอาจอนุญาตให้ไอน์สไตน์ในหมู่พวกเราใช้ API ที่มีความสามารถมากขึ้นซึ่งอาจแสดงออกผ่านทาง DSL API หลักใน Scala นั้นอยู่ไกลไปตามเส้นทางนี้แล้ว
การนำเสนอ@higherkindedในหัวข้อ - การพิจารณาประสิทธิภาพของ Scalaซึ่งเปรียบเทียบ Java / Scala
เครื่องมือ
บล็อกโพสต์ยอดเยี่ยม:
Java และ Scala ทั้งคู่รวบรวมลงใน JVM bytecode ดังนั้นความแตกต่างก็ไม่ใหญ่มากนัก การเปรียบเทียบที่ดีที่สุดที่คุณจะได้รับอาจเป็นเกมเกณฑ์มาตรฐานภาษาคอมพิวเตอร์ซึ่งโดยพื้นฐานแล้วบอกว่า Java และ Scala ทั้งคู่มีการใช้หน่วยความจำเท่ากัน Scala นั้นช้ากว่า Java เล็กน้อยในบางส่วนของเกณฑ์มาตรฐานที่ระบุไว้ แต่นั่นอาจเป็นเพราะการใช้งานโปรแกรมนั้นแตกต่างกัน
ถึงแม้ว่าพวกเขาทั้งคู่จะสนิทกัน แต่ก็ไม่ควรกังวล ผลผลิตที่เพิ่มขึ้นที่คุณได้รับจากการใช้ภาษาที่แสดงออกเช่น Scala นั้นมีค่ามากกว่าการตีประสิทธิภาพ (ถ้ามี) น้อยที่สุด
Java and Scala both compile down to JVM bytecode,
ซึ่งรวมกับ a so
ถึงคำแถลงในคำถามที่diffence isn't that big.
ฉันต้องการแสดงให้เห็นว่านั่นso
เป็นเพียงแค่การใช้ถ้อยคำโวหารและไม่ใช่ข้อสรุปเชิงโต้แย้ง
ตัวอย่าง Java ไม่ใช่สำนวนจริงสำหรับโปรแกรมประยุกต์ทั่วไป รหัสที่ได้รับการปรับให้เหมาะสมดังกล่าวอาจพบได้ในวิธีการไลบรารีระบบ แต่มันจะใช้อาเรย์ของประเภทที่ถูกต้องนั่นคือไฟล์ [] และจะไม่โยน IndexOutOfBoundsException (เงื่อนไขตัวกรองที่แตกต่างกันสำหรับการนับและการเพิ่ม) เวอร์ชันของฉันจะเป็น (เสมอ (!) ที่มีเครื่องหมายปีกกาเนื่องจากฉันไม่ต้องการใช้เวลาหนึ่งชั่วโมงในการค้นหาข้อผิดพลาดซึ่งได้รับการแนะนำโดยการบันทึก 2 วินาทีเพื่อกดปุ่มเดียวใน Eclipse):
List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
if(s.length() > 2) {
File file = mapping.get(s);
if (file != null) {
bigEnough.add(file);
}
}
}
แต่ฉันสามารถนำตัวอย่างโค้ด Java ที่น่าเกลียดอื่น ๆ มาให้คุณได้มากมายจากโครงการปัจจุบันของฉัน ฉันพยายามหลีกเลี่ยงการคัดลอกและปรับเปลี่ยนสไตล์การเขียนโค้ดโดยแยกโครงสร้างและพฤติกรรมออกจากกัน
ในคลาสฐาน DAO แบบนามธรรมของฉันฉันมีคลาสในนามธรรมสำหรับกลไกการแคชทั่วไป สำหรับวัตถุทุกประเภทของแบบจำลองคอนกรีตจะมีคลาสย่อยของคลาสฐาน DAO ที่เป็นนามธรรมซึ่งคลาสชั้นในถูกซับคลาสเพื่อจัดเตรียมการนำไปใช้งานสำหรับวิธีการที่สร้างวัตถุธุรกิจเมื่อโหลดจากฐานข้อมูล (เราไม่สามารถใช้เครื่องมือ ORM เนื่องจากเราเข้าถึงระบบอื่นผ่าน API ที่เป็นกรรมสิทธิ์)
คลาสย่อยและรหัสการสร้างอินสแตนซ์ไม่ชัดเจนใน Java และจะสามารถอ่านได้อย่างมากใน Scala