web-dev-qa-db-ja.com

正確に制御された実行時間でJavaプログラムを起動するには?

Javaアプリケーションを起動する前にJVMを知っているので、JVMはRAMの一部を割り当てます。このメモリは、起動前にユーザーが制御できます。しかし、アプリを起動すると、毎回実行時間が異なるという別の問題があります。

Forループを使用した非常に簡単な例を次に示します。

package timespent;

public class Main {

    public static void main(String[] args) {

        long startTime = System.nanoTime();

        int j = 0;

        for (int i = 0; i < 1.E6; i++) {
            j = i + 1;
        }

        long endTime = System.nanoTime();

        long duration = (endTime - startTime);

        System.out.println("duration = " + duration);
    }
}

さまざまな結果が出力されます。

duration = 5720542
duration = 7331307
duration = 7737946
duration = 6899173

正確に10,000,000ナノ秒または10ミリ秒で実行したいとします。

私が欲しいものは?

Javaアプリを正確な時間で実行したい。

なぜこれが必要なのですか?

アプリを起動するとき、すべてのコンポーネントを読み込む前に、アプリケーションの起動ウィンドウに残っている正確な実行時間を表示したいと思います。

これは一種のCPU操作であり、可能かどうかを知りたかったのです。

Q1: Javaでは可能ですか?
Q2: Javaで不可能な場合、OSネイティブメソッドにアクセスしてこれを達成する方法はありますか。例えばJavaアプリケーションなどに優先順位を付けますか?
Q3:アプリケーションの状態をファイルに保存し、メモリにロードする方法は?

21
Asad Ganiev

時間測定には多くの不確実性の原因があります。そして、これらのソースはすべて測定に影響するだけでなく、不確かなのはランタイムそのものです。不確実性の原因には次のものがあります。

  • キャッシュ使用量(メモリのどの部分がCPU内にキャッシュされるか)。 CPUがバックグラウンドタスクを実行することにより、キャッシュからデータを追い出すことができます。

  • メモリの配置(メモリは実行中のCPUコアに直接接続されていますか?)プロセスはいつでも別のコアに移行される可能性があるため、これは時間の経過とともに変化する可能性があります。

  • ソフトウェア割り込み(OSがプロセスを先取りして別のプロセスを実行する)。静かなマシンで実行することで多少緩和できますが、中断されないという保証はありません。

  • サーマルスロットリング(CPUが高温すぎると判断し、クロック速度を低下させます)。クロック速度が固定された組み込みプロセッサで作業する準備ができていない限り、実際にこれについてできることはあまりありません。

  • ハードウェアの割り込み(ネットワークコネクタがインターネット上の別のマシンからデータを受信しました)。あなたは、これがいつストライキするかにまったく影響を与えません。

  • 予測不可能なレイテンシー(ディスクからデータを読み取っていますが、最初に、データが読み取りヘッドの下に到達するまでディスクを待機する必要があります)。これは、まったく同じアクションを何度も繰り返す場合のパターンに従う可能性がありますが、無関係なハードウェア割り込みが発生すると、1/7200 rpm * 60 s/min = 8.3 ms

  • ガベージコレクション(Javaについて質問しているため、バックグラウンドで実行されているGCがあります)。最良の、最新のガベージコレクターでさえ、時々世界を止めることを完全に避けることはできません。そして、彼らが世界を止めないときでさえ、彼らはまだバックグラウンドで走り、キャッシュ、メモリ配置、およびソフトウェア割り込みを介してランタイムノイズを導入します。

これらはおそらく最も重要な情報源であり、他にもあるかもしれません。ポイントは、あなたのプロセスはマシン上でneverだけだということです。 OSなしで実行し、すべてのハードウェア割り込みを無効にしない限り、実行時間は実行ごとに異なるという事実に耐える必要があり、それを修正する方法はありません。

40
cmaster

単に不可能です。まず、ナノ秒単位で時間を測定することは正確ではありません。 この投稿 はそれをうまく説明しているように感じます。

次に、CPUが実行をスケジュールする方法を制御できません。 CPU時間を占有する他のタスクが存在する可能性があり、これによりプログラムの実行が遅延します。

16

任意のコードの正確な実行時間は、物理マシンが同時に実行している他のことに依存するため、非決定的です。

スタートアップタイムスタンプと計画された終了タイムスタンプを追跡し、プログラムを終了するまでメインスレッドをスリープさせて実行時間を「一定」にすることを計画した場合でも、かなり大きく変化します。

スレッドが実行または待機するタイミングと時間は、プログラマーの制御外です。

9
Bohemian

[TL; DR]それは非常に困難/不可能です。

より長い答えは、以下を含むいくつかの問題について 経路追加博士論文による平坦性試験-ベンチマーク方法論の章 を参照してください。

  1. リソースを持つ他のアプリケーション。つまりメモリが不足しており、オペレーティングシステムがアプリケーションをページングする必要がある。または、別のアプリケーションがCPUを共有しているため、コードの実行が遅くなります。
  2. クロック解像度-コードは、CPUが可能な限り高速で実行されます。別のコンピューターに移植すると、ベンチマークによって大幅に異なる結果が得られる可能性があるため、システムに合わせて最適化しないでください。
  3. クラスのロード-JVMが最初にコードを実行するとき、クラスをメモリ(通常はディスクから)にロードし、バイトコードを解析する必要があるため、最初の実行は後続の時間よりも大幅に遅くなります。
  4. Just-In-Timeコンパイル-JVMが最初にバイトコードをロードすると、純粋に解釈されたモードで実行されます。バイトコードのブロック(つまり、コード内の1つの関数)を複数回(10,000回など)実行すると、その関数をネイティブコードにコンパイルできます。コンパイルにより実行速度は低下しますが、解釈されたコードではなくネイティブコードを実行しているため、その後の呼び出しは高速になります。ただし、これは1回限りのコンパイルではなく、ブロック内の特定の実行パスが優先されていることをJVMが検出し​​た場合、バイトコードを再コンパイルしてそれらのパスを最適化しようとするため、バイトのネイティブバージョンが複数になる可能性がありますJVMが統計に対してコードを安定させるまでコードを作成します。
  5. ガベージコレクション-Javaがガベージコレクタを呼び出している間にコードが中断されることがあります。

そのため、アプリケーションのベンチマークを実行して、最適に実行される方法を確認するには、次のようにします。

  • 他のアプリケーションをできるだけ多く停止します。
  • コードを何万回も実行します。
  • 最初の10,000-20,000の実行を無視します(クラスの読み込みとJITのコンパイルを軽減するため)。そして
  • ガベージコレクションが発生した場合は、反復を無視します(これは見た目よりも判断が困難です)。

最適なパフォーマンスのアイデアが得られますが、最適なものと現実の世界は2つの非常に異なるものです。

6
MT0

これに近づく唯一の方法は、リアルタイム実行をサポートするように特別に設計されたオペレーティングシステムで リアルタイムJava を使用することです。

5
Matt McHenry

他の人が言ったように、プログラムの速度に影響する他の要因のため、残りの正確な時間を知ることは不可能です。ただし、たとえばWindowsでファイルの大きなディレクトリをコピーするときや大きなファイルをダウンロードするときに行われるように、マイルストーンを入れて過去の実行を参照し、以前の実行と比較してこの実行までの実際の時間の分散から計算される準正確な時間を取得することができますChromeのファイル。

したがって、正確な問題を特定することはできませんが、インターネット上のサードパーティシステムに接続する必要がある100,000の操作を処理するようなもので、完了までに通常15分ほどかかるとします。 1)開始時間、2)予想終了時間、3)完了した部分を追跡できます。ですから、1/2が終わったら、経過時間をとって、それがどれだけ残っているかを言うことができます。基本的には、1秒あたりのオペレーション数を取得し、それで残りのオペレーションを除算して、残りの秒数を取得します。

_double speed = completedOperations / elapsedSeconds;
double remainingSeconds = remainingOperations / speed;
_

ただし、これは変更される可能性があります。プロセスを開始し、コンピューターのディスクだけでなくインターネット接続もスラッシングするだけでなく、オフサイトバックアップが開始されるまでに5分で1/4を経過したとします。現在、物事は1/10の速度で処理されています。推定完了時間は20分から始まり、5分で15分になります。ただし、その時点で速度が低下するため、30分後には1/2しか完了せず、その時点での残り時間は30分になります。ここで、バックアップが完了したと言うと、実際には10分で完了しますが、30分が残っていると表示されます。

このような問題を回避する方法はありません。それを緩和するためにいくつかのことを行うことができます。処理の最後の30秒間だけ速度を上げたい場合があります。物事が現在の速度で継続する場合、それは最も正確です。それが問題である場合、その日の時間の平均速度を歴史的に記録できます。速度が変動する場合、合計走行速度と直前の速度を平均できます。

それをスローするかもしれないもう一つは、データの分散です。たとえば、顧客を見て、顧客になった日付に基づいて処理する場合、最初の10,000件の操作は、何年も一緒にいて、処理する大量のデータがある忠実な顧客に対して行われ、最後の10,000件は新規顧客になる可能性がありますより高速に処理するデータがほとんどありません。その後、顧客数の代わりに基礎となるデータ量を使用できます...


ただし、何らかの理由(たいていの場合)で正確になりたい場合は、時間をかけて偽造することもできます。最大の通常の実行時間を取り、開始からの経過時間と残り時間を提供するために開始からかかった時間を使用します。その後、すべての実際の作業が完了したら、sleep()コマンドを入力して残りの時間を待ちます。ただし、システムの負荷などが原因で非常に長い時間がかかる可能性は常にありますが、最大時間をその新しい値に変更できます。

おそらく、実行の時間は何らかの曲線を描いており、時間を長くすればするほど、その時間内に単一の実行が完了する可能性が高くなりますが、その後、実行の多くは何も待たなくなります:

_         #            ^
         #            |
        ###           |
        ###           |
       #####          R
      #######         U
    ###########       N
###################   S

Time-------------->
_

これはばかげているように見えますが、制御できない変数のためにアプリケーションが異なる速度で実行されます。また、ほぼ一定の実行時間が重要な場合、これが唯一の方法です。

3
Jason Goemaat

うまくいかないでしょう。それは、プログラムで動作しているシステムリソースの量など、システムの状態に依存します。

ご覧のとおり、アプリケーションを開くまでの残り時間を表示する必要があります。その場合、2人のユーザーが異なるコア構造とクロック周波数を持つ異なるマシンでプログラムを実行していると仮定します...

しかし、私は提案をすることができます、あなたはあなたのプログラムのデータを読んで、それに基づいて時間を調整することができます。他のアプリケーションのように、..%がロードされていることを示すか、ダウンロードされた..%を示すダウンロードマネージャー機能と同じです.

1
rajiv baghel