#ifdef #ifndef di Java

106

Saya ragu apakah ada cara untuk membuat kondisi waktu kompilasi di Java seperti #ifdef #ifndef di C ++.

Masalah saya adalah bahwa ada algoritma yang ditulis di Java, dan saya memiliki waktu berjalan yang berbeda untuk meningkatkan algoritma itu. Jadi saya ingin mengukur berapa banyak waktu yang saya hemat ketika setiap peningkatan digunakan.

Saat ini saya memiliki satu set variabel boolean yang digunakan untuk memutuskan selama waktu berjalan mana yang harus digunakan dan mana yang tidak. Tetapi bahkan menguji variabel-variabel itu mempengaruhi total waktu berjalan.

Jadi saya ingin mencari cara untuk memutuskan selama waktu kompilasi bagian mana dari program yang harus dikompilasi dan digunakan.

Apakah ada yang tahu cara melakukannya di Java. Atau mungkin seseorang tahu bahwa tidak ada cara seperti itu (itu juga akan berguna).

jutky
sumber

Jawaban:

126
private static final boolean enableFast = false;

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

Persyaratan seperti yang ditunjukkan di atas dievaluasi pada waktu kompilasi. Jika sebaliknya Anda menggunakan ini

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

Kemudian setiap kondisi yang bergantung pada enableFast akan dievaluasi oleh kompiler JIT. Overhead untuk ini dapat diabaikan.

Mark Thornton
sumber
Solusi ini lebih baik daripada solusi saya. Ketika saya mencoba untuk menginisialisasi variabel dengan nilai luar yang telah ditentukan, waktu berjalan kembali ke 3 detik. Tetapi ketika saya mendefinisikan variabel sebagai variabel kelas statis (dan bukan variabel lokal fungsi) waktu berjalan kembali ke 1 detik. Terima kasih untuk bantuannya.
Jutky
6
IIRC, ini bahkan bekerja sebelum Java memiliki compiler JIT. Kode tersebut telah dihapus oleh javacsaya pikir. Ini hanya berfungsi jika ekspresi untuk (katakanlah) enableFastadalah ekspresi konstanta waktu kompilasi.
Stephen C
2
Ya, tetapi persyaratan ini harus berada dalam suatu metode, benar? Bagaimana dengan kasus di mana kami memiliki sekumpulan String final statis pribadi yang ingin kami setel. (misalnya, sekumpulan URL server yang disetel berbeda untuk produksi vs. pementasan)
tomwhipple
3
@tomwhipple: benar, ditambah ini tidak memungkinkan Anda untuk melakukan sesuatu seperti: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko
3
bagaimana dengan impor (misalnya, terkait dengan classpath)?
n611x007
44

javac tidak akan menampilkan kode terkompilasi yang tidak dapat dijangkau. Gunakan variabel terakhir yang ditetapkan ke nilai konstan untuk Anda #definedan ifpernyataan normal untuk #ifdef.

Anda dapat menggunakan javap untuk membuktikan bahwa kode yang tidak dapat dijangkau tidak disertakan dalam file kelas keluaran. Misalnya, perhatikan kode berikut:

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 memberikan keluaran berikut, menunjukkan bahwa hanya satu dari dua jalur yang dikompilasi (dan pernyataan if tidak):

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
Phil Ross
sumber
2
Apakah ini khusus untuk javac, atau apakah perilaku ini sebenarnya dijamin oleh JLS?
Pacerier
@pacerier, saya tidak tahu apakah ini dijamin oleh JLS, tetapi itu benar untuk setiap kompiler java yang saya temui sejak tahun 90-an, dengan kemungkinan pengecualian sebelum 1.1.7, dan hanya karena saya tidak melakukannya uji itu.
12

Saya pikir saya telah menemukan solusinya, Ini jauh lebih sederhana.
Jika saya mendefinisikan variabel boolean dengan pengubah "final", kompilator Java itu sendiri memecahkan masalah. Karena mengetahui terlebih dahulu apa hasil dari pengujian kondisi ini. Contoh kode ini:

    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++;
    }

berjalan sekitar 3 detik di komputer saya.
Dan yang satu ini

    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++;
    }

berjalan sekitar 1 detik. Waktu yang sama dengan kode ini

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }
jutky
sumber
1
Itu menarik. Sepertinya JIT sudah mendukung kompilasi bersyarat! Apakah berhasil jika final tersebut ada di kelas lain atau paket lain?
joeytwiddle
Bagus! Maka saya percaya ini harus menjadi pengoptimalan runtime, kode sebenarnya tidak dilucuti pada waktu kompilasi. Tidak apa-apa selama Anda menggunakan VM dewasa.
joeytwiddle
@joeytwiddle, Kata kunci adalah "selama Anda menggunakan" VM dewasa.
Pacerier
2

Tidak pernah menggunakannya, tapi ini ada

JCPP adalah implementasi Java dari C preprocessor yang lengkap, patuh, mandiri, dan murni. Ini dimaksudkan untuk digunakan oleh orang-orang yang menulis kompiler C-style di Java menggunakan alat seperti sablecc, antlr, JLex, CUP dan sebagainya. Proyek ini telah digunakan untuk melakukan praproses dengan sukses banyak kode sumber pustaka GNU C. Pada versi 1.2.5, itu juga dapat memproses pustaka Apple Objective C.

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

Tom
sumber
1
Saya tidak yakin ini sesuai dengan kebutuhan saya. Kode saya ditulis di Java. Mungkin Anda mengusulkan saya untuk mendapatkan sumber mereka dan menggunakannya untuk memproses kode saya sebelumnya?
Jutky
2

Jika Anda benar-benar membutuhkan kompilasi bersyarat dan Anda menggunakan Ant , Anda mungkin dapat memfilter kode Anda dan melakukan pencarian-dan-ganti di dalamnya.

Misalnya: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Dalam cara yang sama Anda dapat, misalnya, menulis filter untuk menggantikan LOG.debug(...);dengan /*LOG.debug(...);*/. Ini masih akan mengeksekusi lebih cepat daripada if (LOG.isDebugEnabled()) { ... }barang, belum lagi menjadi lebih ringkas pada saat bersamaan.

Jika Anda menggunakan Maven , ada fitur serupa yang dijelaskan di sini .

rustyx.dll
sumber
2

Manifold menyediakan preprocessor Java yang terintegrasi penuh (tidak ada langkah build atau sumber yang dihasilkan). Ini secara eksklusif menargetkan kompilasi bersyarat dan menggunakan arahan gaya C.

Preprocessor Java Manifold

Scott
sumber
1

Gunakan Pola Pabrik untuk beralih antar implementasi kelas?

Waktu pembuatan objek tidak bisa menjadi perhatian sekarang bukan? Ketika dirata-ratakan dalam jangka waktu yang lama, komponen terbesar dari waktu yang dihabiskan seharusnya ada di algoritme utama sekarang bukan?

Sebenarnya, Anda tidak benar-benar membutuhkan preprocessor untuk melakukan apa yang ingin Anda capai. Kemungkinan besar ada cara lain untuk memenuhi kebutuhan Anda selain yang telah saya usulkan tentunya.

jldupont.dll
sumber
Perubahannya sangat kecil. Seperti menguji beberapa kondisi untuk mengetahui terlebih dahulu hasil yang diminta alih-alih menghitung ulang. Jadi overhead panggilan ke fungsi tersebut mungkin tidak cocok untuk saya.
Jutky
0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
alicanbatur.dll
sumber