สมมติว่ามีสตริง เราจะดำเนินการประกบลูปจำนวนมากกับสตริงนี้ การใช้ "+" จะทำให้ได้ประสิทธิภาพต่ำที่สุด แต่ผลงานนี้แย่ขนาดไหน? จะเกิดอะไรขึ้นถ้าเราใส่ StringBuffer, StringBuilder หรือ String.concat() เข้าไปในการทดสอบประสิทธิภาพด้วย? บทความนี้จะให้คำตอบสำหรับคำถามเหล่านี้!
เราจะใช้ Per4j ในการคำนวณประสิทธิภาพ เนื่องจากเครื่องมือนี้สามารถให้ชุดตัวบ่งชี้ประสิทธิภาพที่ครบถ้วน เช่น เวลาต่ำสุดและสูงสุดที่ใช้ ค่าเบี่ยงเบนมาตรฐานของช่วงเวลาทางสถิติ เป็นต้น ในโค้ดทดสอบ เพื่อให้ได้ค่าเบี่ยงเบนมาตรฐานที่แม่นยำ เราจะทำการทดสอบการประกบ "*" 20 ครั้ง 50,000 ครั้ง นี่คือวิธีที่เราจะใช้ในการต่อสตริง:
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัวดำเนินการต่อข้อมูล (+)
วิธี concat สตริง concat (String str)
StringBuffer วิธีการผนวกผนวก (String str)
StringBuilder วิธีการผนวกผนวก (String str)
สุดท้ายนี้ เราจะดูที่ bytecode เพื่อดูว่าวิธีการเหล่านี้ถูกดำเนินการอย่างไร ตอนนี้เรามาเริ่มต้นด้วยการสร้างชั้นเรียนของเรา โปรดทราบว่าในการคำนวณประสิทธิภาพของแต่ละลูป แต่ละโค้ดทดสอบในโค้ดจะต้องถูกรวมเข้ากับไลบรารี Per4J ขั้นแรกเรากำหนดจำนวนการวนซ้ำ
คัดลอกรหัสรหัสดังต่อไปนี้:
int สุดท้ายคงที่ส่วนตัว OUTER_ITERATION=20;
int สุดท้ายคงที่ส่วนตัว INNER_ITERATION=50000;
ต่อไป เราจะใช้ 4 วิธีข้างต้นเพื่อนำโค้ดทดสอบของเราไปใช้
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง addTestStr = "";
สตริง concatTestStr = "";
StringBuffer concatTestSb = null;
StringBuilder concatTestSbu = null;
สำหรับ (int ภายนอกIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = ใหม่ LoggingStopWatch("StringAddConcat");
addTestStr = "";
สำหรับ (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
addTestStr += "*";
stopWatch.หยุด();
-
สำหรับ (int ภายนอกIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = ใหม่ LoggingStopWatch("StringConcat");
concatTestStr = "";
สำหรับ (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestStr.concat("*");
stopWatch.หยุด();
-
สำหรับ (int ภายนอกIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = ใหม่ LoggingStopWatch("StringBufferConcat");
concatTestSb = StringBuffer ใหม่ ();
สำหรับ (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSb.ผนวก("*");
stopWatch.หยุด();
-
สำหรับ (int ภายนอกIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = ใหม่ LoggingStopWatch("StringBuilderConcat");
concatTestSbu = StringBuilder ใหม่ ();
สำหรับ (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSbu.ผนวก("*");
stopWatch.หยุด();
-
จากนั้นให้รันโปรแกรมเพื่อสร้างการวัดประสิทธิภาพ สภาพแวดล้อมการทำงานของฉันคือระบบปฏิบัติการ Windows 7 64 บิต, เครื่อง JVM (7-ea) 32 บิต พร้อมหน่วยความจำ 4GB และ CPU Quad-core Quad 2.00GHz
มันออกมาสมบูรณ์แบบอย่างที่เราจินตนาการไว้ สิ่งที่น่าสนใจเพียงอย่างเดียวคือทำไม String.concat ถึงดีมาก เราทุกคนรู้ดีว่า String เป็นคลาสคงที่ (คลาสที่จะไม่เปลี่ยนแปลงหลังจากการกำหนดค่าเริ่มต้น) แล้วเหตุใดประสิทธิภาพของ concat จึงดีกว่า (หมายเหตุผู้แปล: อันที่จริง มีปัญหากับโค้ดทดสอบของผู้เขียนต้นฉบับ โค้ดทดสอบสำหรับเมธอด concat() ควรเขียนเป็น concatTestStr=concatTestStr.concat(“*”)) เพื่อที่จะตอบคำถามนี้ คำถาม เราควรดูที่ concat decompilation ที่ bytecode ออกมา แพ็คเกจดาวน์โหลดของบทความนี้ประกอบด้วยรหัสไบต์ทั้งหมด แต่ตอนนี้เรามาดูข้อมูลโค้ดของ concat นี้กันดีกว่า:
คัดลอกรหัสรหัสดังต่อไปนี้:
46: ใหม่ #6; //คลาส java/lang/StringBuilder
49: ซ้ำ
50: เรียกใช้พิเศษ #7; //Method java/lang/StringBuilder"<init>":()V
53: aload_1
54: เรียกใช้เสมือน #8; //วิธีการ java/lang/StringBuilder.append:
(ลจาวา/lang/String;)ลจาวา/lang/StringBuilder;
57: ldc #9; //สตริง *
59: เรียกใช้เสมือน #8; //วิธีการ java/lang/StringBuilder.append:
(ลจาวา/lang/String;)ลจาวา/lang/StringBuilder;
62: เรียกใช้เสมือน #10; //Method java/lang/StringBuilder.toString:()
จาวา/lang/สตริง;
65: astore_1
66: iinc 7, 1
69: ไปที่ 38
รหัสนี้เป็น bytecode ของ String.concat() จากโค้ดนี้ เราจะเห็นได้อย่างชัดเจนว่าเมธอด concat() ใช้ StringBuilder ประสิทธิภาพของ concat() ควรจะดีเท่ากับ StringBuilder แต่เนื่องจากการสร้าง StringBuilder เพิ่มเติมและ การทำ .append(str).append(str).toString() การดำเนินการจะส่งผลต่อประสิทธิภาพของการต่อกันดังนั้น StringBuilder และ String เวลา Cancate คือ 1.8 และ 3.3
ดังนั้น แม้ว่าจะทำการต่อข้อมูลที่ง่ายที่สุด หากเราไม่ต้องการสร้างอินสแตนซ์ StringBuffer หรือ StringBuilder เราก็ควรใช้ concat แต่สำหรับการดำเนินการประกบสตริงจำนวนมาก เราไม่ควรใช้ concat (หมายเหตุผู้แปล: เนื่องจากโค้ดทดสอบไม่เทียบเท่ากันอย่างสมบูรณ์ในฟังก์ชัน เวลาประมวลผลเฉลี่ยของ concat ในโค้ดทดสอบที่ถูกแทนที่คือ 1,650.9 มิลลิวินาที ผลลัพธ์นี้อยู่ใน ความคิดเห็นข้อความต้นฉบับ ) เนื่องจาก concat จะลดประสิทธิภาพของโปรแกรมและใช้ CPU ของคุณ ดังนั้น เพื่อให้ได้ประสิทธิภาพสูงสุดโดยไม่ต้องคำนึงถึงความปลอดภัยของเธรดและการซิงโครไนซ์ เราควรลองใช้ StringBuilder