#ifdef #ifndef ใน Java


106

ฉันสงสัยว่ามีวิธีสร้างเงื่อนไขเวลาคอมไพล์ใน Java เช่น #ifdef #ifndef ใน C ++ หรือไม่

ปัญหาของฉันคือมีอัลกอริทึมที่เขียนด้วย Java และฉันมีเวลาในการทำงานที่แตกต่างกันในการปรับปรุงอัลกอริทึมนั้น ดังนั้นฉันต้องการวัดว่าฉันประหยัดเวลาได้เท่าใดเมื่อใช้การปรับปรุงแต่ละครั้ง

ตอนนี้ฉันมีชุดของตัวแปรบูลีนที่ใช้ในการตัดสินใจในช่วงเวลาที่ทำงานว่าควรใช้การปรับปรุงใดและไม่ควรใช้อะไร แต่แม้กระทั่งการทดสอบตัวแปรเหล่านั้นก็มีผลต่อเวลาทำงานทั้งหมด

ดังนั้นฉันจึงต้องการหาวิธีตัดสินใจในช่วงเวลาการรวบรวมว่าควรคอมไพล์และใช้ส่วนใดของโปรแกรม

มีใครรู้วิธีทำใน Java หรืออาจมีคนรู้ว่าไม่มีวิธีนี้ (ก็จะมีประโยชน์เช่นกัน)

คำตอบ:


126
private static final boolean enableFast = false;

// ...
if (enableFast) {
  // This is removed at compile time
}

เงื่อนไขตามที่แสดงด้านบนจะได้รับการประเมินในเวลาคอมไพล์ ถ้าคุณใช้สิ่งนี้แทน

private static final boolean enableFast = "true".equals(System.getProperty("fast"));

จากนั้นเงื่อนไขใด ๆ ที่ขึ้นอยู่กับ enableFast จะถูกประเมินโดยคอมไพเลอร์ JIT ค่าใช้จ่ายสำหรับสิ่งนี้มีค่าเล็กน้อย


ทางออกนี้ดีกว่าแล้วของฉัน เมื่อฉันพยายามเริ่มต้นตัวแปรด้วยค่าภายนอกที่ตั้งไว้ล่วงหน้าเวลาในการทำงานจะย้อนกลับไปเป็น 3 วินาที แต่เมื่อฉันกำหนดตัวแปรเป็นตัวแปรคลาสแบบคงที่ (ไม่ใช่ตัวแปรภายในของฟังก์ชัน) เวลาทำงานจะกลับมาเป็น 1 วินาที ขอบคุณสำหรับความช่วยเหลือ
jutky

6
IIRC ซึ่งใช้งานได้ก่อนที่ Java จะมีคอมไพเลอร์ JIT javacฉันคิดว่ารหัสถูกลบออกไป สิ่งนี้ใช้ได้ผลก็ต่อเมื่อนิพจน์สำหรับ (พูด) enableFastเป็นนิพจน์คงที่ของเวลาคอมไพล์
Stephen C

2
ใช่ แต่เงื่อนไขนี้ต้องอยู่ในเมธอดใช่ไหม แล้วกรณีที่เรามีสตริงสุดท้ายแบบคงที่ส่วนตัวจำนวนมากที่เราต้องการตั้งค่า (เช่นชุด URL ของเซิร์ฟเวอร์ที่ตั้งค่าแตกต่างกันสำหรับการใช้งานจริงเทียบกับการจัดเตรียม)
tomwhipple

3
@tomwhipple: จริงบวกกับสิ่งนี้ไม่อนุญาตให้คุณทำสิ่งที่ชอบ: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
สิ่งที่เกี่ยวกับการนำเข้า (เช่นเกี่ยวกับ classpath)
n611x007

44

javac จะไม่แสดงโค้ดที่คอมไพล์แล้วซึ่งไม่สามารถเข้าถึงได้ ใช้ชุดตัวแปรสุดท้ายเป็นค่าคงที่สำหรับคำสั่งของคุณ#defineและifคำสั่งปกติสำหรับ#ifdef.

คุณสามารถใช้ javap เพื่อพิสูจน์ว่าโค้ดที่ไม่สามารถเข้าถึงได้ไม่รวมอยู่ในไฟล์คลาสเอาต์พุต ตัวอย่างเช่นพิจารณารหัสต่อไปนี้:

public class Test
{
   private static final boolean debug = false;

   public static void main(String[] args)
   {
       if (debug) 
       {
           System.out.println("debug was enabled");
       }
       else
       {
           System.out.println("debug was not enabled");
       }
   }
}

javap -c Test ให้ผลลัพธ์ต่อไปนี้ซึ่งระบุว่ามีเพียงหนึ่งในสองเส้นทางที่คอมไพล์ใน (และคำสั่ง if ไม่ได้):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String debug was not enabled
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

2
javac นี้เฉพาะหรือพฤติกรรมนี้รับรองโดย JLS หรือไม่?
Pacerier

@pacerier ฉันไม่รู้ว่าสิ่งนี้รับประกันโดย JLS หรือไม่ แต่มันเป็นความจริงสำหรับคอมไพเลอร์ java ทุกตัวที่ฉันเจอมาตั้งแต่ยุค 90 โดยมีข้อยกเว้นที่เป็นไปได้ก่อนหน้า 1.1.7 และเพียงเพราะฉันไม่ได้ ทดสอบแล้ว

12

ฉันคิดว่าฉันพบวิธีแก้ปัญหาแล้วมันง่ายกว่ามาก
ถ้าฉันกำหนดตัวแปรบูลีนด้วยคอมไพเลอร์ Java ตัวปรับแต่ง "ขั้นสุดท้าย" จะช่วยแก้ปัญหาได้ เพราะมันรู้ล่วงหน้าว่าจะเป็นผลจากการทดสอบเงื่อนไขนี้อย่างไร ตัวอย่างเช่นรหัสนี้:

    boolean flag1 = true;
    boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

ทำงานประมาณ 3 วินาทีบนคอมพิวเตอร์ของฉัน
และอันนี้

    final boolean flag1 = true;
    final boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

ทำงานประมาณ 1 วินาที เวลาเดียวกันกับรหัสนี้

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }

1
นั่นน่าสนใจ. ดูเหมือนว่า JIT รองรับการคอมไพล์ตามเงื่อนไขแล้ว! ได้ผลหรือไม่หากรอบชิงชนะเลิศอยู่ในคลาสอื่นหรือแพ็คเกจอื่น
joeytwiddle

เยี่ยมมาก! จากนั้นฉันเชื่อว่านี่ต้องเป็นการเพิ่มประสิทธิภาพรันไทม์รหัสไม่ได้ถูกตัดออกในเวลาคอมไพล์ ใช้ได้ตราบเท่าที่คุณใช้ VM ที่เป็นผู้ใหญ่
joeytwiddle

@joeytwiddle คำหลักคือ "ตราบใดที่คุณใช้" VM ที่เป็นผู้ใหญ่
Pacerier

2

ไม่เคยใช้ แต่มีอยู่

JCPP คือการใช้งาน Java แบบสแตนด์อโลนที่สมบูรณ์และเป็นไปตามมาตรฐานของ C พรีโปรเซสเซอร์ มีวัตถุประสงค์เพื่อใช้กับผู้ที่เขียนคอมไพเลอร์สไตล์ C ใน Java โดยใช้เครื่องมือเช่น sablecc, antlr, JLex, CUP และอื่น ๆ โปรเจ็กต์นี้ถูกใช้เพื่อประมวลผลซอร์สโค้ดของไลบรารี GNU C ล่วงหน้าได้สำเร็จ ในเวอร์ชัน 1.2.5 ยังสามารถประมวลผลไลบรารี Apple Objective C ล่วงหน้าได้อีกด้วย

http://www.anarres.org/projects/jcpp/


1
ฉันไม่แน่ใจว่าสิ่งนี้เหมาะกับความต้องการของฉัน รหัสของฉันเขียนด้วยภาษาจาวา บางทีคุณอาจจะเสนอให้ฉันหาแหล่งที่มาและใช้เพื่อประมวลผลโค้ดของฉันล่วงหน้า
jutky

2

หากคุณต้องการการคอมไพล์ตามเงื่อนไขจริงๆและคุณใช้Antคุณอาจสามารถกรองโค้ดของคุณและทำการค้นหาและแทนที่ได้

ตัวอย่างเช่นhttp://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

ในลักษณะเดียวกับที่คุณสามารถยกตัวอย่างเช่นเขียนกรองเพื่อแทนที่ด้วยLOG.debug(...); /*LOG.debug(...);*/สิ่งนี้ยังคงดำเนินการได้เร็วกว่าif (LOG.isDebugEnabled()) { ... }สิ่งต่างๆไม่ต้องพูดถึงการรัดกุมมากขึ้นในเวลาเดียวกัน

หากคุณใช้Mavenจะมีคุณลักษณะที่คล้ายกันนี้อธิบายไว้ที่นี่ที่นี่


2

Manifoldจัดเตรียมพรีโปรเซสเซอร์ Java แบบครบวงจร (ไม่มีขั้นตอนการสร้างหรือซอร์สที่สร้างขึ้น) กำหนดเป้าหมายเฉพาะการคอมไพล์ตามเงื่อนไขและใช้คำสั่งสไตล์ C

Java Preprocessor ของ Manifold


1

ใช้รูปแบบโรงงานเพื่อสลับระหว่างการใช้งานคลาสหรือไม่

เวลาในการสร้างวัตถุไม่สามารถเป็นกังวลได้ในตอนนี้หรือไม่? เมื่อเฉลี่ยในช่วงเวลาที่ใช้งานยาวนานองค์ประกอบที่ใหญ่ที่สุดของเวลาที่ใช้ควรอยู่ในอัลกอริทึมหลักในตอนนี้ใช่หรือไม่

พูดอย่างเคร่งครัดคุณไม่จำเป็นต้องมีตัวประมวลผลล่วงหน้าเพื่อทำสิ่งที่คุณต้องการเพื่อให้บรรลุ อาจมีวิธีอื่น ๆ ในการตอบสนองความต้องการของคุณมากกว่าวิธีที่ฉันเสนอแน่นอน


การเปลี่ยนแปลงเป็นเรื่องเล็กน้อยมาก เช่นเดียวกับการทดสอบเงื่อนไขบางอย่างเพื่อให้ทราบล่วงหน้าถึงผลลัพธ์ที่ร้องขอแทนที่จะคำนวณใหม่ ดังนั้นค่าใช้จ่ายในการเรียกฟังก์ชันจึงไม่เหมาะกับฉัน
jutky

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.