การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล
Blackhole และสถานะ
การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
การหลีกเลี่ยงการลบโค้ดที่ไม่มีการใช้งาน
กับดักที่ใหญ่ที่สุดของการทดสอบประสิทธิภาพระดับย่อยคือการลบโค้ดที่ไม่มีการใช้งาน (DCE): หากคอมไพเลอร์แบบทันเวลาพิสูจน์ได้ว่าไม่มีการใช้ผลลัพธ์ มันจะลบการคำนวณนั้นออกไป จากนั้น Benchmark ของคุณก็จะไม่วัดอะไรเลย JMH มีเครื่องมือสองอย่างเพื่อป้องกันปัญหานี้ ได้แก่ การส่งคืนค่าและ Blackhole
กับดัก DCE
ลูปนี้คำนวณรากที่สองหนึ่งล้านครั้ง แต่ไม่เคยใช้ผลลัพธ์เลย คอมไพเลอร์อัจฉริยะจึงสามารถลบลูปทั้งหมดออกได้
public class Main {
public static void main(String[] args) {
long t = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
double ignored = Math.sqrt(i); // result thrown away
}
System.out.println("ns: " + (System.nanoTime() - t));
}
}วิธีแก้ที่ 1: ส่งคืนผลลัพธ์
วิธีป้องกันที่ง่ายที่สุดคือส่งคืนค่าที่คำนวณได้จากเมธอด @Benchmark JMH จะใช้ค่าที่ส่งคืนทุกค่า ดังนั้น JVM จึงไม่สามารถพิสูจน์ได้ว่าค่านั้นไม่มีการใช้งาน
import org.openjdk.jmh.annotations.Benchmark;
public class Bench {
@Benchmark
public double sqrt() {
return Math.sqrt(42.0); // returned -> consumed by JMH
}
}วิธีแก้ที่ 2: แบล็กโฮล
เมื่อ Benchmark สร้างค่าหลายค่า หรือเมื่อคุณไม่สามารถส่งคืนค่าเดียวได้ ให้แทรก Blackhole แล้วเรียกใช้ consume(...) แบล็กโฮลจะทำให้คอมไพเลอร์แบบทันเวลาเชื่อว่ามีการใช้ค่าทุกค่า โดยไม่ต้องเสียต้นทุนจากการทำงานจริง
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;
public class Bench {
@Benchmark
public void manyValues(Blackhole bh) {
for (int i = 0; i < 100; i++) {
bh.consume(Math.sqrt(i));
}
}
}ออบเจ็กต์ @State
คลาส @State จะเก็บอินพุตที่ไม่เปิดเผยรายละเอียดต่อตัวปรับให้เหมาะสม เพื่อป้องกันการพับค่าคงที่ JMH จะสร้างอินสแตนซ์ของคลาสนี้และส่งให้ Benchmark ของคุณ ขอบเขตอาจเป็น Thread, Benchmark (ใช้ร่วมกัน) หรือ Group
import org.openjdk.jmh.annotations.*;
public class Bench {
@State(Scope.Thread)
public static class Data {
public int x = 21;
}
@Benchmark
public int multiply(Data d) {
return d.x * 2; // d.x is not a compile-time constant
}
}@Setup และ @TearDown
ภายในคลาส @State เมธอดที่มีคำอธิบายประกอบ @Setup จะทำงานก่อนการวัดผล และ @TearDown จะทำงานหลังจากนั้น Level จะควบคุมความถี่ ได้แก่ Trial (ครั้งเดียว), Iteration หรือ Invocation
import org.openjdk.jmh.annotations.*;
@State(Scope.Thread)
public class Data {
int[] arr;
@Setup(Level.Trial)
public void init() {
arr = new int[1000];
for (int i = 0; i < arr.length; i++) arr[i] = i;
}
}หลีกเลี่ยงอินพุตค่าคงที่
อย่าป้อนค่าคงที่ที่เขียนตรง ๆ ให้กับเมธอดที่กำลังทดสอบโดยเด็ดขาด Math.sqrt(2.0) อาจถูกพับรวมเป็นค่าคงที่ได้ แต่ Math.sqrt(state.value) ไม่สามารถทำเช่นนั้นได้ ควรรับอินพุตจากฟิลด์ @State เสมอ
การคืนค่าหลายค่า
หากการทดสอบประสิทธิภาพของคุณสร้างผลลัพธ์สองค่าโดยธรรมชาติ คุณสามารถคืนค่าหนึ่งค่าแล้วส่งอีกค่าหนึ่งให้แบล็กโฮล หรือรวมผลลัพธ์ทั้งสองเข้าด้วยกันได้ หากปล่อยให้ค่าใดค่าหนึ่งไม่ได้ถูกใช้ ช่องโหว่จาก DCE ก็จะกลับมาอีกครั้ง
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;
public class Bench {
@Benchmark
public int twoResults(Blackhole bh) {
int a = 3 * 7;
int b = 5 + 9;
bh.consume(b);
return a;
}
}โอเวอร์เฮดของแบล็กโฮล
แบล็กโฮลได้รับการออกแบบให้มีต้นทุนต่ำมากและป้องกันการปรับปรุงประสิทธิภาพที่อาจตัดทิ้งตัวมันเองได้ แต่ก็ไม่ได้ไม่มีต้นทุนโดยสิ้นเชิง สำหรับการทำงานที่รวดเร็วมาก ควรเลือกคืนค่าเพียงค่าเดียว และใช้แบล็กโฮลกับลูปหรือกรณีที่มีหลายค่า
ตรวจสอบความสมเหตุสมผลของตัวเลข
หากการทดสอบประสิทธิภาพรายงานเวลาเหลือน้อยจนเป็นไปไม่ได้ เช่น ต่ำกว่าหนึ่งนาโนวินาทีสำหรับงานจริง ให้สงสัยว่าอาจเกิด DCE หรือการพับค่าคงที่ ตรวจสอบอีกครั้งว่ามีการคืนค่าหรือใช้ผลลัพธ์ และอินพุตมาจาก @State
นำทุกอย่างมารวมกัน
การทดสอบประสิทธิภาพที่รัดกุมควรอ่านอินพุตจากออบเจ็กต์ @State ทำงานที่ต้องการ แล้วคืนค่าผลลัพธ์ด้วย return หรือส่งผลลัพธ์ให้ Blackhole.consume ด้วยแนวปฏิบัติสามข้อนี้ DCE และการพับค่าคงที่จะไม่สามารถทำให้การวัดของคุณคลาดเคลื่อนได้
ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจของคุณเกี่ยวกับการป้องกันการตัดโค้ดที่ไม่มีการใช้งาน
ทบทวน
คุณได้เรียนรู้วิธีทำให้การทดสอบประสิทธิภาพมีความน่าเชื่อถือ:
- การตัดโค้ดที่ไม่มีการใช้งานจะลบการคำนวณที่ไม่ได้ใช้
- คืนค่าผลลัพธ์เพียงค่าเดียว เพื่อให้ JMH รับผลลัพธ์นั้นไปใช้
- ใช้ Blackhole.consume กับลูปและกรณีที่มีหลายค่า
- ออบเจ็กต์ @State ทำให้อินพุตไม่ชัดเจน จึงป้องกันการพับค่าคงที่
- @Setup / @TearDown ใช้เตรียมและล้างสถานะในระดับที่เลือก
คำถามที่พบบ่อย
บทเรียน “การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล”
Blackhole และสถานะ คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดจึงใช้ JMH
- การเขียนการวัดประสิทธิภาพ
- การอุ่นเครื่องและรอบการทำงาน
- การหลีกเลี่ยงการลบโค้ดที่ไม่ก่อผล