คำว่า "สเกลเว็บ" ได้รับการ hyped เมื่อเร็ว ๆ นี้และผู้คนยังทำให้ระบบของพวกเขา "โดเมนกว้าง" มากขึ้นโดยการขยายสถาปัตยกรรมแอปพลิเคชันของพวกเขา แต่โดเมนเต็มรูปแบบคืออะไร? หรือวิธีการให้แน่ใจว่าโดเมนทั้งหมด?
โหลดการปรับสเกลมากที่สุดคือโดเมนที่ได้รับความนิยมมากที่สุด ตัวอย่างเช่นระบบที่รองรับการเข้าถึงผู้ใช้เดี่ยวสามารถรองรับการเข้าถึงผู้ใช้ 10, 100 หรือ 1 ล้านคน ตามหลักการแล้วระบบของเราควรยังคงเป็น "ไร้สัญชาติ" มากที่สุด แม้ว่ารัฐจะต้องมีอยู่ แต่ก็สามารถแปลงและส่งผ่านในเทอร์มินัลการประมวลผลที่แตกต่างกันของเครือข่าย เมื่อโหลดกลายเป็นคอขวดอาจไม่มีความล่าช้า ดังนั้นสำหรับคำขอเดียวจึงเป็นที่ยอมรับได้ว่าจะใช้เวลา 50 ถึง 100 มิลลิวินาที สิ่งนี้เรียกว่าการปรับขนาด
ส่วนขยายดำเนินการแตกต่างกันอย่างสิ้นเชิงในการเพิ่มประสิทธิภาพโดเมนเต็มรูปแบบ ตัวอย่างเช่นอัลกอริทึมที่ทำให้มั่นใจได้ว่าข้อมูลหนึ่งข้อมูลได้รับการประมวลผลสำเร็จสามารถประมวลผลข้อมูล 10, 100 หรือ 1 ล้านข้อมูลได้สำเร็จ ความซับซ้อนของเหตุการณ์ (สัญลักษณ์ O ขนาดใหญ่) เป็นคำอธิบายที่ดีที่สุดโดยไม่คำนึงว่าประเภทตัวชี้วัดนี้เป็นไปได้หรือไม่ เวลาแฝงเป็นนักฆ่าเครื่องปรับขนาดประสิทธิภาพ คุณจะพยายามอย่างเต็มที่เพื่อประมวลผลการดำเนินการทั้งหมดในเครื่องเดียวกัน สิ่งนี้เรียกว่าการปรับขนาด
หากพายสามารถตกลงมาจากท้องฟ้า (แน่นอนว่าเป็นไปไม่ได้) เราอาจสามารถรวมการขยายตัวในแนวนอนและการขยายตัวในแนวดิ่ง อย่างไรก็ตามวันนี้เราจะแนะนำวิธีง่ายๆต่อไปนี้เพื่อปรับปรุงประสิทธิภาพ
ทั้ง ForkJoinpool ใน Java 7 และสตรีมข้อมูลแบบขนานใน Java 8 (ParallelsTream) มีประโยชน์สำหรับการประมวลผลแบบขนาน เป็นที่ชัดเจนโดยเฉพาะอย่างยิ่งเมื่อปรับใช้โปรแกรม Java บนโปรเซสเซอร์หลายคอร์เนื่องจากโปรเซสเซอร์ทั้งหมดสามารถเข้าถึงหน่วยความจำเดียวกันได้
ดังนั้นประโยชน์พื้นฐานของการประมวลผลแบบขนานนี้คือการกำจัดเวลาแฝงเกือบทั้งหมดเมื่อเทียบกับการปรับขนาดบนเครื่องจักรที่แตกต่างกันในเครือข่าย
แต่อย่าสับสนกับผลกระทบของการประมวลผลแบบขนาน! โปรดจำไว้ว่าสองจุดต่อไปนี้:
การลดความซับซ้อนของอัลกอริทึมเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการปรับปรุงประสิทธิภาพ ตัวอย่างเช่นสำหรับวิธีการค้นหาอินสแตนซ์ HashMap () ความซับซ้อนของเหตุการณ์ o (1) หรือความซับซ้อนของอวกาศ o (1) เป็นวิธีที่เร็วที่สุด แต่สถานการณ์นี้มักเป็นไปไม่ได้
หากคุณไม่สามารถลดความซับซ้อนของอัลกอริทึมได้คุณสามารถปรับปรุงประสิทธิภาพได้โดยการค้นหาประเด็นสำคัญในอัลกอริทึมและวิธีการปรับปรุง สมมติว่าเรามีไดอะแกรมอัลกอริทึมต่อไปนี้:
ความซับซ้อนของเวลาโดยรวมของอัลกอริทึมนี้คือ O (N3) และหากคำนวณในลำดับการเข้าถึงแยกต่างหากก็สามารถสรุปได้ว่าความซับซ้อนคือ O (N X O X P) แต่อย่างไรก็ตามเราจะพบสถานการณ์แปลก ๆ เมื่อเราวิเคราะห์รหัสนี้:
หากไม่มีการอ้างอิงข้อมูลการผลิตเราอาจสรุปได้อย่างง่ายดายว่าเราควรเพิ่มประสิทธิภาพ "การดำเนินการสูงของหัว" แต่การเพิ่มประสิทธิภาพที่เราทำไม่ได้มีผลต่อผลิตภัณฑ์ที่ส่งมอบ
กฎทองที่ดีที่สุดนั้นไม่มีอะไรมากไปกว่าดังต่อไปนี้:
นั่นคือทั้งหมดสำหรับทฤษฎี สมมติว่าเราพบว่าปัญหาเกิดขึ้นในสาขาที่ถูกต้องเป็นไปได้ว่าการประมวลผลอย่างง่ายในผลิตภัณฑ์จะสูญเสียการตอบสนองเนื่องจากเวลาจำนวนมาก (สมมติว่าค่าของ N, O และ P มีขนาดใหญ่มาก) โปรดทราบว่าความซับซ้อนของเวลาของสาขาด้านซ้ายที่กล่าวถึงในบทความคือ O (N3) ความพยายามที่ทำไว้ที่นี่ไม่สามารถขยายได้ แต่สามารถประหยัดเวลาสำหรับผู้ใช้และชะลอการปรับปรุงประสิทธิภาพที่ยากจนกระทั่งในภายหลัง
นี่คือ 10 เคล็ดลับสำหรับการปรับปรุงประสิทธิภาพของ Java:
1. ใช้ StringBuilder
ควรใช้ StingBuilder โดยค่าเริ่มต้นในรหัส Java ของเราและควรหลีกเลี่ยงผู้ประกอบการ + บางทีคุณอาจมีความคิดเห็นที่แตกต่างกันเกี่ยวกับน้ำตาลไวยากรณ์ของ StringBuilder เช่น:
สตริง x = "a" + args.length + "b";
จะถูกรวบรวมเป็น:
0 java.lang.stringbuilder ใหม่ [16] 3 dup4 ldc <string "a"> [18] 6 invokespecial java.lang.stringbuilder (java.lang.string) [20] 9 aload_0 [args] 10 arraylength11 java.lang.stringbuilder [23] 14 ldc <string "b"> [27] 16 invokevirtual java.lang.stringbuilder.append (java.lang.string): java.lang.stringbuilder [29] 19 [32] 22 Store_1 [x]
แต่เกิดอะไรขึ้นกันแน่? คุณต้องใช้ส่วนต่อไปนี้เพื่อปรับปรุงสตริงถัดไปหรือไม่?
สตริง x = "a" + args.length + "b"; ถ้า (args.length == 1) x = x + args [0];
ตอนนี้เราได้ใช้สตริงที่สองซึ่งไม่ได้ใช้หน่วยความจำพิเศษในกอง แต่สร้างแรงกดดันต่อ GC
StringBuilder x = new StringBuilder ("A"); X.Append (args.length); x.append ("b"); ถ้า (args.length == 1); x.append (args [0]);ในตัวอย่างข้างต้นหากคุณพึ่งพาคอมไพเลอร์ Java เพื่อสร้างอินสแตนซ์โดยปริยายเอฟเฟกต์ที่รวบรวมจะไม่มีส่วนเกี่ยวข้องกับการใช้อินสแตนซ์ StringBuilder หรือไม่ โปรดจำไว้ว่า: ในสาขา Nope เวลาที่ใช้ในแต่ละรอบ CPU จะถูกใช้ไปกับ GC หรือจัดสรรพื้นที่เริ่มต้นสำหรับ StringBuilder และเรากำลังเสียเวลา
โดยทั่วไปแล้วการใช้ StringBuilder นั้นดีกว่าการใช้ตัวดำเนินการ + หากเป็นไปได้ให้เลือก StringBuilder หากคุณต้องการผ่านการอ้างอิงในหลาย ๆ วิธีเนื่องจากสตริงใช้ทรัพยากรเพิ่มเติม Jooq ใช้วิธีนี้เมื่อสร้างคำสั่ง SQL ที่ซับซ้อน มีการใช้สตริงเพียงหนึ่งครั้งในระหว่างการส่งผ่าน SQL ทั้งหมดของแผนผังนามธรรมแบบนามธรรม
สิ่งที่น่าเศร้ายิ่งกว่านั้นคือถ้าคุณยังคงใช้ StringBuffer ให้ใช้ StringBuilder แทน StringBuffer ท้ายที่สุดมีหลายกรณีที่จำเป็นต้องซิงโครไนซ์
การแสดงออกอย่างสม่ำเสมอทำให้ผู้คนรู้สึกว่าพวกเขารวดเร็วและรวดเร็ว แต่การใช้การแสดงออกอย่างสม่ำเสมอในสาขา Nope จะเป็นการตัดสินใจที่เลวร้ายที่สุด หากคุณต้องใช้นิพจน์ทั่วไปในรหัสที่ต้องคำนวณอย่างน้อยแคชรูปแบบเพื่อหลีกเลี่ยงการรวบรวมรูปแบบซ้ำ ๆ
รูปแบบสุดท้ายคงที่ heavy_regex = pattern.compile ("((((x)*y)*z)*");หากใช้นิพจน์ทั่วไปแบบธรรมดาเช่นนี้:
String [] parts = ipaddress.split ("//.");นี่เป็นการดีที่สุดที่จะใช้อาร์เรย์ถ่านปกติหรือการดำเนินการตามดัชนี ตัวอย่างเช่นรหัสต่อไปนี้ที่มีความสามารถในการอ่านไม่ดีมีบทบาทเดียวกัน
ความยาว int = ipaddress.length (); int onfset = 0; int part = 0; สำหรับ (int i = 0; i <length; i ++) {ถ้า (i == ความยาว - 1 || ipaddress.charat (i+1) == '.') {ส่วน [ส่วน] = ipaddress.substringรหัสข้างต้นยังแสดงให้เห็นว่าการเพิ่มประสิทธิภาพก่อนวัยอันควรไม่มีความหมาย แม้ว่าจะเปรียบเทียบกับวิธีการแยก () รหัสนี้สามารถบำรุงรักษาได้น้อยกว่า
ความท้าทาย: สมาร์ทเพื่อนสามารถสร้างอัลกอริทึมที่เร็วขึ้นได้หรือไม่?
การแสดงออกปกติมีประโยชน์มาก แต่พวกเขาก็ต้องจ่ายราคาเมื่อใช้ โดยเฉพาะอย่างยิ่งเมื่อคุณอยู่ลึกลงไปในสาขาไม่คุณควรหลีกเลี่ยงการใช้นิพจน์ทั่วไปในราคาทั้งหมด นอกจากนี้ยังต้องระวังวิธีการสตริง JDK ต่างๆที่ใช้นิพจน์ทั่วไปเช่น string.replaceall () หรือ string.split () คุณสามารถเลือกที่จะใช้ห้องสมุดการพัฒนาที่ได้รับความนิยมมากขึ้นเช่น Apache Commons Lang เพื่อดำเนินการสตริง
ข้อเสนอแนะนี้ใช้ไม่ได้กับโอกาสทั่วไป แต่เฉพาะกับสถานการณ์ที่ลึกลงไปในสาขา Nope ถึงกระนั้นคุณควรเข้าใจบางสิ่งบางอย่าง วิธีการเขียนรูปแบบ Java 5 นั้นสะดวกมากจนเราสามารถลืมวิธีการวนซ้ำภายในเช่น:
สำหรับ (ค่าสตริง: สตริง) {// ทำสิ่งที่เป็นประโยชน์ที่นี่}เมื่อใดก็ตามที่รหัสทำงานในลูปนี้หากตัวแปรสตริงเป็นแบบวนซ้ำรหัสจะสร้างอินสแตนซ์ของตัววนซ้ำโดยอัตโนมัติ หากคุณใช้ ArrayList เครื่องเสมือนจะจัดสรรหน่วยความจำประเภทจำนวนเต็ม 3 โดยอัตโนมัติไปยังวัตถุบนกอง
คลาสส่วนตัว ITR ใช้ Iterator <E> {int Cursor; int lastret = -1; int คาดหวัง modCount = modCount; -นอกจากนี้คุณยังสามารถใช้วิธีการวนซ้ำที่เทียบเท่าต่อไปนี้เพื่อแทนที่ด้านบนสำหรับลูปเพียงแค่ "เสีย" ชิ้นส่วนของการทำศัลยกรรมพลาสติกบนสแต็กซึ่งค่อนข้างคุ้มค่า
ขนาด int = strings.size (); สำหรับ (int i = 0; i <size; i ++) {ค่าสตริง: strings.get (i); // ทำสิ่งที่เป็นประโยชน์ที่นี่}หากค่าของสตริงในลูปไม่เปลี่ยนแปลงมากนักคุณสามารถใช้อาร์เรย์เพื่อใช้ลูปได้
สำหรับ (ค่าสตริง: StringArray) {// ทำสิ่งที่เป็นประโยชน์ที่นี่}ไม่ว่าจะมาจากมุมมองของการอ่านและการเขียนที่ง่ายหรือจากมุมมองของการออกแบบ API, ตัววนซ้ำ, อินเทอร์เฟซที่ทำซ้ำและลูป foreach มีประโยชน์มาก แต่ด้วยค่าใช้จ่ายเมื่อใช้พวกเขาวัตถุเพิ่มเติมจะถูกสร้างขึ้นบนกองสำหรับเด็กวนแต่ละคน หากการวนซ้ำจะถูกดำเนินการหลายครั้งโปรดระวังเพื่อหลีกเลี่ยงการสร้างอินสแตนซ์ที่ไม่มีความหมาย เป็นการดีที่สุดที่จะใช้วิธีการวนรอบตัวชี้พื้นฐานเพื่อแทนที่ตัววนซ้ำที่กล่าวถึงข้างต้นอินเตอร์เฟสที่ทำซ้ำและ foreach loop
ความคิดเห็นบางอย่างที่คัดค้านข้างต้น (โดยเฉพาะอย่างยิ่งการแทนที่ตัววนซ้ำด้วยพอยน์เตอร์) จะถูกกล่าวถึงใน Reddit เพื่อดูรายละเอียด
บางวิธีมีราคาแพง การใช้สาขา Nope เป็นตัวอย่างเราไม่ได้พูดถึงวิธีการที่เกี่ยวข้องของใบไม้ แต่สามารถพบได้ สมมติว่าไดรเวอร์ JDBC ของเราจำเป็นต้องเอาชนะความยากลำบากทั้งหมดในการคำนวณค่าส่งคืนของวิธี Resultet.wasnull () เฟรมเวิร์ก SQL ที่เราใช้เองอาจมีลักษณะเช่นนี้:
if (type == integer.class) {result = (t) Wasnull (rs, integer.valueof (rs.getInt (ดัชนี))));} // และจากนั้น ... คงที่ <t> t Wasnull (ผลลัพธ์ RS, ค่า t) โยน sqlexception NULL: ค่า;}ในตรรกะข้างต้นวิธีการ resultet.wasnull () เรียกว่าทุกครั้งที่ค่า int ได้รับจากชุดผลลัพธ์ แต่วิธีการของ getint () ถูกกำหนดเป็น:
ประเภทส่งคืน: ค่าตัวแปร; หากผลลัพธ์การสืบค้น SQL เป็นโมฆะให้ส่งคืน 0
ดังนั้นวิธีที่เรียบง่ายและมีประสิทธิภาพในการปรับปรุงมีดังนี้:
คงที่ <t ขยายหมายเลข> t Wasnull (ResultSet Rs, ค่า t) พ่น sqlexception {return (value == null || (value.intValue () == 0 && rs.wasnull ()))? NULL: ค่า;}นี่คือสายลม
วิธีแคชเรียกใช้วิธีการแทนที่วิธีค่าใช้จ่ายสูงบนโหนดใบหรือหลีกเลี่ยงการเรียกใช้วิธีการสูงกว่าหากการประชุมวิธีการอนุญาต
ข้างต้นแนะนำการใช้งานทั่วไปจำนวนมากในตัวอย่างจาก Jooq ส่งผลให้การใช้งานของคลาส byte, short, int และยาว แต่อย่างน้อยชื่อสามัญไม่ควรเป็นข้อ จำกัด ด้านรหัสจนกว่าพวกเขาจะมีความเชี่ยวชาญในโครงการ Java 10 หรือ Valhalla เพราะสามารถแทนที่ด้วยวิธีการต่อไปนี้:
// เก็บไว้ในจำนวนเต็มฮีป i = 817598;
... ถ้าคุณเขียนด้วยวิธีนี้:
// เก็บบนสแต็ก int i = 817598;
สิ่งต่างๆอาจแย่ลงเมื่อใช้อาร์เรย์:
// สามวัตถุถูกสร้างขึ้นบนจำนวนเต็มฮีป [] i = {1337, 424242};... ถ้าคุณเขียนด้วยวิธีนี้:
// มีเพียงวัตถุเดียวเท่านั้นที่สร้างขึ้นบนฮีปอิน [] i = {1337, 424242};เมื่อเราอยู่ลึกลงไปในสาขา Nope เราควรพยายามอย่างเต็มที่เพื่อหลีกเลี่ยงการใช้คลาสบรรจุภัณฑ์ ข้อเสียของการทำเช่นนี้คือมันทำให้เกิดแรงกดดันอย่างมากต่อ GC GC จะยุ่งมากในการล้างวัตถุที่สร้างขึ้นโดยคลาสบรรจุภัณฑ์
ดังนั้นวิธีการเพิ่มประสิทธิภาพที่มีประสิทธิภาพคือการใช้ชนิดข้อมูลพื้นฐานอาร์เรย์ความยาวคงที่และใช้ชุดของตัวแปรแยกเพื่อระบุตำแหน่งของวัตถุในอาร์เรย์
Trove4J ซึ่งเป็นไปตามโปรโตคอล LGPL เป็นไลบรารีคลาส Java Collection ซึ่งให้การใช้งานประสิทธิภาพที่ดีกว่าเรามากกว่าการสร้างอาร์เรย์ int []
ข้อยกเว้นต่อไปนี้สำหรับกฎนี้: เนื่องจากประเภทบูลีนและไบต์ไม่เพียงพอที่จะอนุญาตให้ JDK ให้วิธีแคชสำหรับมัน เราสามารถเขียนด้วยวิธีนี้:
บูลีน a1 = true; // ... น้ำตาลไวยากรณ์สำหรับ: บูลีน a2 = boolean.valueof (จริง); byte b1 = (ไบต์) 123; // ... น้ำตาลไวยากรณ์สำหรับ: byte b2 = byte.valueof ((ไบต์) 123);
จำนวนเต็มประเภทพื้นฐานอื่น ๆ ยังมีสถานการณ์ที่คล้ายกันเช่นถ่าน, สั้น, int และยาว
อย่าใส่กล่องแบบดั้งเดิมจำนวนเต็มเหล่านี้โดยอัตโนมัติเมื่อเรียกตัวสร้างหรือเรียกใช้วิธี thetype.valueof ()
นอกจากนี้อย่าเรียกตัวสร้างในคลาส wrapper เว้นแต่คุณต้องการรับอินสแตนซ์ที่ไม่ได้สร้างขึ้นบนกอง ข้อได้เปรียบของการทำเช่นนี้คือการให้เรื่องตลกวันเมษายนคนโง่ครั้งใหญ่แก่เพื่อนร่วมงานของคุณ
แน่นอนว่าหากคุณต้องการสัมผัสกับไลบรารีฟังก์ชั่นนอกกองแม้ว่าสิ่งนี้อาจผสมกับการตัดสินใจเชิงกลยุทธ์จำนวนมากแทนที่จะเป็นโซลูชันในแง่ดีที่สุด สำหรับบทความที่น่าสนใจเกี่ยวกับที่เก็บของที่ไม่ใช่กองเขียนโดย Peter Lawrey และ Ben Cotton โปรดคลิก: OpenJDK และ HASHMAP-ให้ทหารผ่านศึกมาสเตอร์อย่างปลอดภัย (ไม่ใช่กองที่เก็บข้อมูล!) เทคนิคใหม่
ทุกวันนี้ภาษาการเขียนโปรแกรมที่ใช้งานได้เช่น Scala ส่งเสริมการเรียกซ้ำ เนื่องจากการเรียกซ้ำมักหมายถึงการกู้คืนหางที่สามารถย่อยสลายเป็นบุคคลที่ได้รับการปรับปรุงเป็นรายบุคคล มันจะดีกว่าถ้าภาษาการเขียนโปรแกรมที่คุณใช้สามารถรองรับได้ แต่ถึงอย่างนั้นโปรดระวังว่าการปรับอัลกอริทึมเล็กน้อยจะเปลี่ยนการเรียกซ้ำหางเป็นซ้ำทั่วไป
หวังว่าคอมไพเลอร์สามารถตรวจจับสิ่งนี้ได้โดยอัตโนมัติมิฉะนั้นเราจะสูญเสียเฟรมสแต็กจำนวนมากสำหรับสิ่งต่าง ๆ ที่สามารถทำได้ด้วยตัวแปรท้องถิ่นเพียงไม่กี่ตัว
ไม่มีอะไรจะพูดในส่วนนี้ยกเว้นว่าการวนซ้ำให้มากที่สุดเท่าที่จะทำได้ในสาขา Nope แทนที่จะเรียกซ้ำ
เมื่อเราต้องการทำซ้ำบนแผนที่ที่บันทึกเป็นคู่คีย์-ค่าเราต้องหาเหตุผลที่ดีสำหรับรหัสต่อไปนี้:
สำหรับ (k key: map.keyset ()) {v value: map.get (key);}ไม่ต้องพูดถึงวิธีการเขียนต่อไปนี้:
สำหรับ (รายการ <k, v> รายการ: map.entryset ()) {k key = entry.getKey (); v value = entry.getValue ();}เมื่อเราใช้ Nope Branch เราควรใช้แผนที่ด้วยความระมัดระวัง เนื่องจากการเข้าถึงการเข้าถึงจำนวนมากที่ดูเหมือนจะมีความซับซ้อนของเวลาของ O (1) ประกอบด้วยชุดปฏิบัติการ และการเข้าถึงนั้นไม่ได้ฟรี อย่างน้อยถ้าคุณต้องใช้แผนที่คุณต้องใช้เมธอด entryset () เพื่อทำซ้ำ! ด้วยวิธีนี้เราต้องการเข้าถึงอินสแตนซ์ของ map.entry เท่านั้น
เมื่อมีความจำเป็นต้องวนซ้ำบนแผนที่ในรูปแบบของคู่คีย์-ค่าให้แน่ใจว่าใช้วิธี entrySet ()
8. ใช้ enumset หรือ enummap
ในบางกรณีเช่นเมื่อใช้แผนที่กำหนดค่าเราอาจรู้ล่วงหน้าค่าคีย์ที่บันทึกไว้ในแผนที่ หากค่าคีย์นี้มีขนาดเล็กมากเราควรพิจารณาใช้ EnumSet หรือ ENUMMAP แทนการใช้ HashSet หรือ HashMap ที่เราใช้กันทั่วไป รหัสต่อไปนี้ให้คำอธิบายที่ชัดเจน:
วัตถุชั่วคราวส่วนตัว [] vals; Public V Put (k key, v value) {// ... int index = key.ordinal (); vals [index] = masknull (value); // ... }การใช้งานหลักของรหัสก่อนหน้าคือเราใช้อาร์เรย์แทนตารางแฮช โดยเฉพาะอย่างยิ่งเมื่อแทรกค่าใหม่ลงในแผนที่สิ่งที่คุณต้องทำคือรับหมายเลขลำดับคงที่ที่คอมไพเลอร์สร้างขึ้นสำหรับแต่ละประเภท enum หากมีการกำหนดค่าแผนที่ทั่วโลก (ตัวอย่างเช่นเพียงหนึ่งอินสแตนซ์) Enummap จะได้รับประสิทธิภาพที่โดดเด่นกว่า HashMap ภายใต้แรงกดดันของการเพิ่มความเร็วในการเข้าถึง เหตุผลก็คือ enummap ใช้หน่วยความจำกองน้อยกว่าบิตหนึ่งบิตกว่า hashmap และ hashmap เรียกเมธอด hashcode () และวิธี Equals () ในแต่ละค่าคีย์
enum และ enummap เป็นเพื่อนสนิท เมื่อเราใช้ค่าคีย์ที่คล้ายกับโครงสร้างที่มีลักษณะคล้าย enum เราควรพิจารณาประกาศค่าคีย์เหล่านี้เป็นประเภท enum และใช้เป็นปุ่ม enummap
ในกรณีที่ไม่สามารถใช้ enummap ได้อย่างน้อยวิธีการ hashcode () และ equals () จะต้องได้รับการปรับให้เหมาะสม วิธีการ hashcode () ที่ดีเป็นสิ่งจำเป็นเพราะมันป้องกันการโทรที่ไม่จำเป็นไปยังวิธีการสูงเท่ากับ ()
ในโครงสร้างการสืบทอดของแต่ละคลาสวัตถุง่าย ๆ ที่ยอมรับได้ง่ายเป็นสิ่งจำเป็น ลองมาดูกันว่า org.jooq.table ของ Jooq ได้รับการใช้งานอย่างไร?
วิธีการใช้งาน HashCode () ที่ง่ายที่สุดและเร็วที่สุดมีดังนี้:
// AbstractTable การใช้งานพื้นฐานของตารางทั่วไป: @Overridepublic int hashCode () {// [#1938] เมื่อเทียบกับ queryParts มาตรฐานนี่คือการใช้งาน HashCode () ที่มีประสิทธิภาพมากขึ้น return name.hashCode ();}ชื่อคือชื่อตาราง เราไม่จำเป็นต้องพิจารณาสคีมาหรือแอตทริบิวต์ตารางอื่น ๆ เนื่องจากชื่อตารางมักจะไม่ซ้ำกันในฐานข้อมูล และชื่อตัวแปรคือสตริงซึ่งตัวเองได้แคชค่า HashCode () แล้ว
ความคิดเห็นในรหัสนี้มีความสำคัญมากเนื่องจากนามธรรมที่สืบทอดมาจาก AbstractQueryPart คือการใช้งานพื้นฐานขององค์ประกอบต้นไม้ไวยากรณ์นามธรรมใด ๆ องค์ประกอบของทรีไวยากรณ์บทคัดย่อสามัญไม่มีคุณลักษณะใด ๆ ดังนั้นพวกเขาจึงไม่สามารถมีจินตนาการเกี่ยวกับการปรับใช้วิธีการ HashCode () ให้เหมาะสม วิธี HashCode ที่เขียนทับ () มีดังนี้:
// AbstractQueryPart การใช้งานพื้นฐานของ Tree Syntax Tree: @overridepublic int hashCode () {// นี่คือการใช้งานเริ่มต้นที่ใช้งานได้ // คลาสย่อยการใช้งานเฉพาะควรแทนที่วิธีนี้เพื่อปรับปรุงประสิทธิภาพ return create (). renderinlined (นี้) .hashCode ();}กล่าวอีกนัยหนึ่งเวิร์กโฟลว์การเรนเดอร์ SQL ทั้งหมดจะถูกเรียกใช้เพื่อคำนวณรหัสแฮชสำหรับองค์ประกอบของทรีไวยากรณ์นามธรรมปกติ
วิธี Equals () นั้นน่าสนใจยิ่งขึ้น:
// การใช้งานขั้นพื้นฐานของตารางทั่วไปนามธรรม: @Overridepublic บูลีนเท่ากับ (วัตถุที่) {ถ้า (นี่ == นั้น) {return true;} // [#2144] ก่อนที่จะเรียกวิธี AbstractQuerypart.equals () หรือไม่ ถ้า (อินสแตนซ์ของ AbstractTable) {ถ้า (StringUtils.equals (ชื่อ, ((((Abstracttable <?>) ที่) .name)))) {return super.equals (นั้น);} return false;} return false;}}ก่อนอื่นอย่าใช้วิธี Equals () เร็วเกินไป (ไม่เพียง แต่ใน NOPE) ถ้า:
หมายเหตุ: หากเราใช้อินสแตนซ์ของเร็วเกินไปที่จะตรวจสอบประเภทที่เข้ากันได้เงื่อนไขต่อไปนี้จะรวมถึงอาร์กิวเมนต์ == null ฉันได้อธิบายสิ่งนี้ไว้แล้วในบล็อกก่อนหน้าของฉันโปรดดู 10 แนวทางปฏิบัติที่ดีที่สุดของ Java ที่ยอดเยี่ยม
หลังจากการเปรียบเทียบสถานการณ์ข้างต้นของเราสิ้นสุดลงเราควรจะสามารถสรุปได้ ตัวอย่างเช่นวิธี table.equals () ของ Jooq ใช้เพื่อเปรียบเทียบว่าสองตารางนั้นเหมือนกันหรือไม่ โดยไม่คำนึงถึงประเภทการใช้งานที่เฉพาะเจาะจงพวกเขาจะต้องมีชื่อฟิลด์เดียวกัน ตัวอย่างเช่นองค์ประกอบสององค์ประกอบต่อไปนี้ไม่เหมือนกัน:
หากเราสามารถพิจารณาได้อย่างง่ายดายว่าพารามิเตอร์ขาเข้าเท่ากับอินสแตนซ์เอง (นี่) เราสามารถยกเลิกการดำเนินการได้หรือไม่หากผลลัพธ์เป็นเท็จ หากผลการส่งคืนเป็นจริงเราสามารถตัดสินการใช้งานชั้นนำของชั้นเรียนได้อย่างยอดเยี่ยม ในกรณีที่วัตถุส่วนใหญ่เปรียบเทียบไม่เท่ากันเราสามารถจบวิธีการโดยเร็วที่สุดเพื่อประหยัดเวลาการดำเนินการของ CPU
วัตถุบางอย่างมีความคล้ายคลึงกันสูงกว่าวัตถุอื่น
ใน Jooq อินสแตนซ์ตารางส่วนใหญ่จะถูกสร้างขึ้นโดยตัวสร้างรหัสของ Jooq และวิธีการ Equals () ของอินสแตนซ์เหล่านี้ได้รับการปรับให้เหมาะสมอย่างยิ่ง ประเภทตารางอื่น ๆ อีกมากมาย (ตารางที่ได้รับ), ฟังก์ชั่นที่มีค่าตาราง, ตารางอาร์เรย์, ตารางที่เข้าร่วม, ตารางเดือย, การแสดงออกของตารางทั่วไป ฯลฯ รักษาการใช้งานพื้นฐานของวิธีการเท่ากับ ()
ในที่สุดก็มีสถานการณ์อื่นที่สามารถนำไปใช้กับทุกภาษาไม่ใช่แค่ Java นอกจากนี้สาขา NOPE ที่เราศึกษาก่อนหน้านี้จะเป็นประโยชน์ในการทำความเข้าใจจาก O (N3) ถึง O (n log n)
น่าเสียดายที่โปรแกรมเมอร์จำนวนมากใช้อัลกอริทึมในท้องถิ่นที่ง่ายเพื่อพิจารณาปัญหา พวกเขาใช้ในการแก้ปัญหาทีละขั้นตอน นี่คือรูปแบบ "ใช่/หรือ" ของความจำเป็น รูปแบบการเขียนโปรแกรมนี้เป็นเรื่องง่ายที่จะสร้างแบบจำลอง "ภาพรวมที่ใหญ่กว่า" เมื่อแปลงจากการเขียนโปรแกรมที่จำเป็นอย่างแท้จริงไปสู่การเขียนโปรแกรมเชิงวัตถุไปสู่การเขียนโปรแกรมที่ใช้งานได้ แต่สไตล์เหล่านี้ขาดสิ่งที่มีอยู่ใน SQL และ R: R: R: R: R: R: R
การเขียนโปรแกรมประกาศ
ใน SQL เราสามารถประกาศผลกระทบที่จำเป็นต่อฐานข้อมูลโดยไม่คำนึงถึงอิทธิพลของอัลกอริทึม ฐานข้อมูลสามารถใช้อัลกอริทึมที่ดีที่สุดตามชนิดข้อมูลเช่นข้อ จำกัด คีย์ดัชนี ฯลฯ
ในทางทฤษฎีก่อนอื่นเรามีแนวคิดพื้นฐานหลังจาก SQL และการคำนวณเชิงสัมพันธ์ ในทางปฏิบัติผู้ขาย SQL ได้ดำเนินการเพิ่มประสิทธิภาพที่มีประสิทธิภาพตามค่าใช้จ่าย (ตัวเพิ่มประสิทธิภาพตามต้นทุน) ในช่วงไม่กี่ทศวรรษที่ผ่านมา จากนั้นในเวอร์ชัน 2010 ในที่สุดเราก็ค้นพบศักยภาพทั้งหมดของ SQL
แต่เราไม่จำเป็นต้องใช้ SQL โดยใช้วิธีการ ทุกภาษาและห้องสมุดสนับสนุนชุดคอลเลกชันกระเป๋าและรายการ ประโยชน์หลักของการใช้ชุดคือมันสามารถทำให้รหัสของเรากระชับและชัดเจน ตัวอย่างเช่นวิธีการเขียนต่อไปนี้:
Someset ตัดกันบางแม่
แทน
// วิธีการเขียนก่อนหน้าของ Java 8 Set Result = new HashSet (); สำหรับ (ผู้สมัครวัตถุ: someset) ถ้า (somotheret.contains (ผู้สมัคร)) result.add (ผู้สมัคร); // แม้ว่า java 8 จะใช้มันก็ไม่เป็นประโยชน์มาก someset.stream (). ตัวกรอง (Syotherset :: มี)
บางคนอาจมีความคิดเห็นที่แตกต่างกันเกี่ยวกับการเขียนโปรแกรมที่ใช้งานได้และ Java 8 ที่สามารถช่วยให้เราเขียนอัลกอริทึมที่ง่ายขึ้นและง่ายขึ้น แต่มุมมองนี้ไม่จำเป็นต้องเป็นจริง เราสามารถแปลง Java 7 Loop ที่จำเป็นให้เป็นคอลเลกชันสตรีมของ Java 8 แต่เรายังคงใช้อัลกอริทึมเดียวกัน แต่การแสดงออกสไตล์ SQL นั้นแตกต่างกัน:
Someset ตัดกันบางแม่รหัสข้างต้นสามารถมีการใช้งานที่แตกต่างกัน 1,000 รายการในเอ็นจิ้นที่แตกต่างกัน สิ่งที่เรากำลังศึกษาอยู่ในวันนี้คือการแปลงสองชุดเป็น enumset โดยอัตโนมัติก่อนที่จะเรียกการดำเนินการ intersect แม้ว่าเราจะสามารถดำเนินการตัดกันแบบขนานได้โดยไม่ต้องเรียกใช้เมธอด Stream.parallel ()
ในบทความนี้เราจะหารือเกี่ยวกับการเพิ่มประสิทธิภาพเกี่ยวกับการแตกแขนงไม่ ตัวอย่างเช่นอัลกอริทึมที่มีความซับซ้อนสูง ในฐานะนักพัฒนาของ Jooq เรามีความยินดีที่จะเพิ่มประสิทธิภาพการสร้าง SQL
Jooq อยู่ที่ "ด้านล่างของห่วงโซ่อาหาร" เพราะเป็น API สุดท้ายที่เรียกโดยโปรแกรมคอมพิวเตอร์ของเราเมื่อออกจาก JVM และเข้าสู่ DBMS ตั้งอยู่ที่ด้านล่างของห่วงโซ่อาหารหมายความว่าเส้นใด ๆ ใช้เวลา n x o x p เวลาเมื่อมันถูกดำเนินการใน Jooq ดังนั้นฉันต้องการเพิ่มประสิทธิภาพโดยเร็วที่สุด
ตรรกะทางธุรกิจของเราอาจไม่ซับซ้อนเท่าสาขา Nope อย่างไรก็ตามเฟรมเวิร์กพื้นฐานอาจซับซ้อนมาก (เฟรมเวิร์ก SQL ท้องถิ่นห้องสมุดท้องถิ่น ฯลฯ ) ดังนั้นเราจำเป็นต้องปฏิบัติตามหลักการที่กล่าวถึงในวันนี้ใช้การควบคุมภารกิจ Java หรือเครื่องมืออื่น ๆ เพื่อตรวจสอบว่ามีพื้นที่ใด ๆ ที่ต้องการการเพิ่มประสิทธิภาพ
ลิงค์ต้นฉบับ: Jaxenter Translation: importNew.com - ลิงก์การแปลบนท้องถนนเสมอ: http://www.importnew.com/16181.html