12

私は、画像の非常に高度な操作を必要とする Java プロジェクトに取り組んでいます。実際、私は OpenCV を使用してほとんどの操作を行っており、JNI を使用して、必要な OpenCV 関数をラップしています。私は OpenCV がもたらすパフォーマンスに非常に満足しています。OpenCV コードを書いた人々は、コードに対して大きな功績を残しました。Java開発者が書いたコードで私が経験したこととは対照的です。

私は自分のプログラミング言語の選択について楽観的に始めました。プロジェクトの最初の作業反復は問題なく動作しましたが、そのパフォーマンスはリアルタイムにはほど遠いものです (2 秒あたり約 1 フレームを取得)。MY コードのいくつかの最適化を行い、それは大いに役立ちました。フレーム レートを 1 秒あたり約 10 ~ 20 フレームまで上げることができました。これは素晴らしいことですが、さらに最適化を行うには、Java コードを書き直して同じことを行う必要があることがわかりました。 -20 倍の効率。

Java の開発者がパフォーマンスにほとんど注意を払っていないことに愕然とします。特にメディア関連のクラスを作成する場合はそうです。OpenJDK をダウンロードし、使用している機能を調べています。たとえば、Raster クラスの下に getPixels(...) という関数があり、画像のピクセルを取得します。私は、この関数がソース コード内で高度に最適化された関数であり、パフォーマンスをさらに最適化するために System.arrayCopy を複数回呼び出すことを期待していました。代わりに、私が見つけたのは非常に「上品な」コードで、1行でできることを達成するために、5〜6の異なるクラスと10〜20の異なるメソッドを呼び出しています。

for (int i =0; i < n; i++) {
  long p = rawFrame[i];
  p = (p << 32) >>> 32;
  byte red = (byte) ((p >> 16) & 0xff);
  byte green = (byte) ((p >> 8) & 0xff);
  byte blue = (byte) ((p) & 0xff);
  byte val = (byte)(0.212671f * red + 0.715160f * green + 0.072169f * blue);
  data[i] = val;
  grayFrameData[i] = (val & 0x80) + (val & (0x7f)); 
}

上記のコードは、画像をグレースケールに変換し、約 1 ~ 10 ミリ秒で浮動ピクセル データを取得します。Java 組み込み関数で同じことをしたい場合、グレースケールへの変換自体に 200 ~ 300 ミリ秒かかり、次に float ピクセルを取得するのに約 50 ~ 100 ミリ秒かかります。これは、リアルタイムのパフォーマンスには受け入れられません。速度を上げるために注意してください。私は、Java 開発者が敬遠するビット単位の演算子を多用しています。

一般的なケースを処理する必要があることは理解していますが、それでも、少なくとも最適化のオプションを提供したり、少なくともこのコードの実行速度がどれほど遅いかを警告したりすることはできません。

私の質問は、開発のこの後半の時点で (私はすでに最初のイテレーションを持っていますが、リアルタイムでより多くのパフォーマンスを発揮する 2 番目のイテレーションに取り組んでいるわけではありません)、弾丸を噛んで C/C++ に切り替える必要があります。もっと調整するか、Java に固執して、スピードアップのために既に実装されている Java コードを書き直す必要がないように、物事がよりリアルタイムに適したものになることを願っています。

Java がいかに「上品」で遅いかということに、私は本当にうんざりし始めています。そこにあるクラスの量は、やり過ぎのようです。

4

9 に答える 9

16

私は Java を使ってコンピューター ビジョンの作業を行ったことがありますが、これを言うと反対票を投じられるかもしれませんが、コンピューター ビジョンやリアルタイムのものには完全に使用できます。使い方を知っていれば十分です。

考えられる最適化:

コードの最適化についてサポートが必要な場合は、喜んでお手伝いします。たとえば、メソッドを作成することでパフォーマンスが向上する可能性があると言えます。

`public static final int getGrayScale(final int pixelRGB){
    return (0.212671f * ((pixelRGB >> 16) & 0xff) + 0.715160f * ((pixelRGB >> 8) & 0xff) + 0.072169f * ((pixelRGB) & 0xff));
}`

for{pixels} ループでこれを使用します。メソッド呼び出しを使用することで、JVM はこの操作をより大幅に最適化でき、おそらく for ループもさらに最適化できます。

書き込む RAM がある場合は、可能なすべての 24 ビット ピクセル ピクセル カラーの出力グレースケール バイトの静的な最終ルックアップ テーブルを作成できます。これは RAM で約 16 MB になりますが、浮動小数点演算を行う必要はなく、単一の配列アクセスだけです。これ、使用している JVM と、配列境界チェックを最適化できるかどうかによって、より高速になる場合があります。

同様の高速な画像処理コードを見つける場所:

ImageJ 画像処理アプリのコード (StackOverflow が遅延しているためリンクできません) とそのライブラリ、具体的には ij.process.TypeConverter を確認することを強くお勧めします。あなたのコードと同じように、ビット操作と最小限の余分な配列作成による直接配列操作に大きく依存しています。Java2D ライブラリ (標準 JRE の一部) と Java Advanced Imaging (JAI) ライブラリ (StackOverflow が遅延しているためリンクできません) は、画像データに対して直接画像処理を行う他の方法を提供します。時間。Java2D の場合は、使用する関数に注意する必要があります。

Java2D ライブラリが間接的である理由:

「高級感」のほとんどは、複数のカラー モデルとストレージ フォーマット (IE HSB 画像、float ベースのカラー モデル、インデックス付きカラー モデル) をサポートしているためです。間接性が存在するのには理由があり、実際にパフォーマンスを向上させることもあります。たとえば、BufferedImage クラスは、最近の VM のグラフィックス メモリに直接フックして、一部の操作を大幅に高速化します。間接化により、多くの場合、これをユーザーから隠すことができます。

于 2009-05-22T15:38:41.693 に答える
6

私の質問は、開発のこの後半の時点で (私はすでに最初のイテレーションを持っていますが、リアルタイムでより多くのパフォーマンスを発揮する 2 番目のイテレーションに取り組んでいるわけではありません)、弾丸を噛んで C/C++ に切り替える必要があります。もっと調整するか、Java に固執して、スピードアップのために既に実装されている Java コードを書き直す必要がないように、物事がよりリアルタイムに適したものになることを願っています。

あなたは私がすべきか尋ねています

  1. 自分のパフォーマンス要件を満たすことができる言語に切り替えます。
  2. Javaに固執し、状況が改善されることを願っています。

他のオプションがあるかもしれません....しかし、オプション2は現実的ではないようです。コードが高速になることを単に「期待」することはできません:p

いくつかの注意点:

  1. OpenJDK のパフォーマンスは必ずしも Sun JDK と同じではありません。Sun JDK を試しましたか?
  2. 実行する必要があるパフォーマンスの最適化がいくつかのメソッドにある場合は、それらを書き直してJavaに固執する価値があるかもしれません...
于 2009-05-21T23:25:27.073 に答える
3

私の提案は、画像の操作がプロジェクト全体と比較してどれほど重要であるかに依存し、Java がもたらすあらゆる利点に関連しています。明らかに、必要に応じて Java で高速なコードを記述できます (実演したように)。ただし、プロジェクトの 80% がそのような最適化で構成されている場合、ここで言語の選択として Java を再考することは間違いありません。

一方、これがアプリケーションの 20% を表し、残りの 80% がこの変換を提供するユーザー機能である場合、操作を完了するために作業を行う必要があることは、対処する必要がないことと引き換えに価値のあるトレードオフです。独自のメモリ管理を使用し、Java がユーザーとのやり取りのために提供する他の A​​PI (Web、Swing、SWT、使用しているものは何でも) を使用します。

Java は、ガベージ コレクターが原因で、そのリアルタイム機能で知られていません。噛まれることもありますので、気をつけてください。

于 2009-05-21T23:19:56.727 に答える
1

パフォーマンスがどれだけ向上するかはわかりませんが、繰り返し実行するプロセスが長時間実行される場合は、を使用してサーバーホットスポットVMを実行してみてくださいjava -server。起動時間を短縮するために最適化されたWindowsのデフォルトであるクライアントVMよりもはるかに優れたパフォーマンスを発揮します。

于 2009-05-22T05:38:43.297 に答える
1

あなたが本当にリアルタイムについて尋ねているかどうかは明らかではありません。リアルタイムとリアルファストには違いがあります。非常に高速な場合、平均的なケースの動作を考慮するだけで十分です。スループットが主な関心事です。リアルタイムとは、毎回決まった時間内に何らかのタスクを完了できることを意味します。もちろん、両方を必要とするアプリケーションもあります。

OpenJDK などの従来の Java 実装では、ガベージ コレクターは、リアルタイム動作を達成するための最大の問題です。これは、ガベージ コレクターがいつでもプログラムを中断して作業を実行できるためです。私の会社 aicas では、ガベージ コレクション用の別のスレッドを必要としない Java を実装しています。代わりに、割り当て時に少しの GC 作業が行われます。事実上、割り当ては、解放されたブロックごとにいくつかのブロックをマークまたはスイープすることによって支払われます。これには、仮想マシンの完全な再実装が必要です。

コンパイルは、リアルタイム Java が従来の Java 実装と異なるもう 1 つのポイントです。リアルタイム Java テクノロジは、JIT コンパイルの代わりに静的または Ahead-of-Time (AoT) コンパイルを使用する傾向があります。最もよく使用されるクラスをコンパイルするために従来の VM で必要とされる「ウォームアップ」時間を許容できる可能性があるため、JiT はアプリケーションにとって問題ない場合があります。そうであれば、おそらくリアルタイム要件はなく、スループット要件だけです。

フレームのデコードがガベージ コレクションによって中断されないようにすることに関心がある場合は、Java のリアルタイム実装と、おそらく AoT コンパイルも使用するのが理にかなっています。Real-Time Specification for Java (RTSJ) は、RelatimeThread、AsyncEventHandler、RawMemoryAccess など、リアルタイムおよび組み込みプログラミングの他のサポートも提供します。

もちろん、リアルタイムであれ非常に高速であれ、優れたパフォーマンスを得るには細部への注意が必要です。一時オブジェクトの過剰使用は役に立ちません。割り当てには常に追加のコストがかかるため、最小限に抑える必要があります。これは、オブジェクトの状態を変更できない関数型言語にとって大きな課題です。ただし、不必要な最適化を避けるために、書かれているコードのクリティカル パスを理解するように注意する必要があります。プロファイリングは、最適化の労力をどこに費やすのが最適かを理解するために不可欠です。

于 2012-06-23T14:59:12.337 に答える
0

私が理解しているように、Java の最新バージョン (またはおそらく JavaFX) には、システムのビデオ ハードウェアの高度な機能にアクセスできるメソッドがあります。私はJava Posseでそれについて聞いたと思います.Java 1.3の世界で立ち往生しているので、実際にそれをチェックする機会はありませんでした.しかし、私はそのようなことを聞​​いたことを覚えています.

これについては次のとおりです。しかし、それは Java 7 でのみのようです :(

また、最初はストリームの再生と基本的なストリーム操作のみをサポートするように見えますが、「しばらく待って Java が改善される」というアプローチが実際に機能する可能性があります。

于 2009-05-21T23:56:10.087 に答える
0

組み込みメソッドを使用する代わりに、使用したいメソッドの最適化されたバージョンを作成することを妨げているのは何ですか? それが不可能な場合は、よりネイティブな言語でオブジェクトを記述し、それを既存のアプリケーションにインポートしてみませんか?

于 2009-05-22T00:04:30.863 に答える