#ifdef #ifndef in Java


106

C ++の#ifdef #ifndefのようなJavaでコンパイル時の条件を作成する方法があるかどうかは疑問です。

私の問題は、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秒に戻りました。助けてくれてありがとう。
2009年

6
IIRC、これはJavaがJITコンパイラーを持つ前にさえ機能しました。コードはjavac私が削除したと思います。これenableFastは、(たとえば)の式がコンパイル時定数式である場合にのみ機能しました。
スティーブンC

2
はい、しかし、この条件はメソッド内に存在する必要がありますよね?設定したいプライベート静的最終文字列がたくさんある場合はどうでしょうか。(例:本番用とステージング用に異なるように設定された一連のサーバーURL)
tomwhipple '19年

3
@tomwhipple:true、それに加えて次のようなことはできません: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
インポートはどうですか(たとえば、クラスパスに関して)?
n611x007 2014年

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 次の出力が表示され、2つのパスのうち1つだけがコンパイルされたことを示します(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 2014年

@pacerier、これがJLSによって保証されているかどうかはわかりませんが、1.1.7より前の可能性のある例外を除いて、90年代以降に遭遇したすべてのJavaコンパイラに当てはまります。それをテストします。

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 2013年

@joeytwiddle、キーワードは成熟したVMを「使用する限り」です。
Pacerier 2014年

2

使用したことはありませんが、これは存在します

JCPPは、Cプリプロセッサの完全で準拠したスタンドアロンのPure Java実装です。これは、sablecc、antlr、JLex、CUPなどのツールを使用してJavaでCスタイルのコンパイラーを作成する人を対象としています。このプロジェクトは、GNU Cライブラリのソースコードの多くを正常に前処理するために使用されています。バージョン1.2.5以降、Apple Objective Cライブラリを前処理することもできます。

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


1
これが私のニーズに合っているかどうかはわかりません。私のコードはJavaで書かれています。多分あなたは私にそれらのソースを取得して私のコードを前処理するためにそれらを使用することを提案していますか?
2009年

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プリプロセッサ


1

ファクトリパターンを使用してクラスの実装を切り替えますか?

オブジェクトの作成時間は今のところ心配することはできませんか?長い実行期間にわたって平均すると、費やされた時間の最大の要素はメインアルゴリズムに含まれるはずです。

厳密に言えば、達成したいことを実行するためのプリプロセッサは実際には必要ありません。もちろん、私が提案した方法以外にも、要件を満たす方法は他にもあるでしょう。


変更はごくわずかです。要求された結果を再計算するのではなく、事前に知るためにいくつかの条件をテストするようなものです。したがって、関数の呼び出しのオーバーヘッドは私には適さない可能性があります。
2009年

0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.