[解決済み] if (variable1 % variable2 == 0)はなぜ非効率的なのですか?
疑問点
私はjavaの初心者で、昨晩いくつかのコードを実行していたのですが、このことがとても気になりました。私はforループでX個の出力を表示する簡単なプログラムを作っていたのですが、modulus を
variable % variable
対
variable % 5000
とかなんとか言ってます。なぜそうなるのか、何が原因なのか、どなたか説明していただけませんか?そうすれば、私はより良くなることができます...
以下は、quot;efficient" のコードです(構文が少し間違っていたらすみません。)
long startNum = 0;
long stopNum = 1000000000L;
for (long i = startNum; i <= stopNum; i++){
if (i % 50000 == 0) {
System.out.println(i);
}
}
以下は、"非効率的なコード"です。
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 50000;
for (long i = startNum; i <= stopNum; i++){
if (i % progressCheck == 0) {
System.out.println(i);
}
}
ちなみに、私は違いを測るために日付変数を用意したのですが、それが十分長くなると、最初のものは50msで、もう一方は12秒かかるとか、そんな感じでした。を増やさないといけないかもしれません。
stopNum
を減らすか
progressCheck
もし、あなたのPCが私より高性能であれば、あるいはそうでなければ。
この質問をウェブ上で探したのですが、答えが見つかりません、私の聞き方が悪いだけかもしれませんが。
編集部 私の質問がこんなに人気があるとは思いませんでした、すべての回答に感謝します。私はそれぞれの半分の時間でベンチマークを実行し、非効率的なコードは1/4秒対10秒とかなり長くかかりました。printlnを使用していることは確かですが、どちらも同じ量を実行しているので、特にこの不一致は再現可能なので、それが大きく影響するとは思えません。答えに関しては、私はJavaの初心者なので、どの答えがベストなのか、今のところ投票に任せることにします。水曜日までに1つ選ぶようにします。
EDIT2: modulusの代わりに変数をインクリメントし、progressCheckに到達したら1回実行し、その変数を0にリセットする第3の選択肢を作るつもりです。
EDIT3.5:
このコードを使って、以下に私の結果を示します。皆さん、素晴らしい助けをありがとうございます また、私は0に長いの短い値を比較しようとしたので、私の新しいチェックは、それが繰り返しで等しくなるように、これまで&quot;65536&quot;回数が発生します。
public class Main {
public static void main(String[] args) {
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 65536;
final long finalProgressCheck = 50000;
long date;
// using a fixed value
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if (i % 65536 == 0) {
System.out.println(i);
}
}
long final1 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
//using a variable
for (long i = startNum; i <= stopNum; i++) {
if (i % progressCheck == 0) {
System.out.println(i);
}
}
long final2 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using a final declared variable
for (long i = startNum; i <= stopNum; i++) {
if (i % finalProgressCheck == 0) {
System.out.println(i);
}
}
long final3 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using increments to determine progressCheck
int increment = 0;
for (long i = startNum; i <= stopNum; i++) {
if (increment == 65536) {
System.out.println(i);
increment = 0;
}
increment++;
}
//using a short conversion
long final4 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if ((short)i == 0) {
System.out.println(i);
}
}
long final5 = System.currentTimeMillis() - date;
System.out.println(
"\nfixed = " + final1 + " ms " + "\nvariable = " + final2 + " ms " + "\nfinal variable = " + final3 + " ms " + "\nincrement = " + final4 + " ms" + "\nShort Conversion = " + final5 + " ms");
}
}
結果
- 固定 = 874ms(通常は1000ms程度だが、2のべき乗であるため速くなる)
- 変数 = 8590 ms
- 最終変数 = 1944 ms (50000を使用した場合、~1000msでした)
- インクリメント = 1904 ms
- ショートコンバージョン=679ms
十分に驚くことではありませんが、分割ができないため、Short Conversion は "fast" 方式よりも 23% 速くなっています。これは興味深いことです。もし、256回ごと(あるいはそのくらい)に何かを表示したり比較したりする必要がある場合は、このようにし
if ((byte)integer == 0) {'Perform progress check code here'}
最終的に興味深いのは、65536(きれいな数字ではありません)で"Final declared Variable"でmodulusを使用すると、固定値より半分の速度(遅い)だったことです。以前はほぼ同じ速度でベンチマークしていたところです。
解決方法は?
を測定しています。 OSR(オンスタック・リプレイスメント) スタブです。
OSRスタブ は、コンパイルされたメソッドの特別なバージョンで、メソッドの実行中に解釈モードからコンパイルされたコードに実行を移すことを特に意図しています。
OSRスタブは、解釈フレームと互換性のあるフレームレイアウトを必要とするため、通常のメソッドほど最適化されていません。このことは、すでに以下の回答で示しました。 1 , 2 , 3 .
ここでも似たようなことが起こります。非効率的なコード"が長いループを実行している間、メソッドはループのすぐ内側でオンスタック置換のために特別にコンパイルされます。解釈されたフレームからOSRコンパイルされたメソッドに状態が転送され、この状態には
progressCheck
ローカル変数 この時点では、JIT は変数を定数に置き換えることができないので、以下のような最適化を適用することができません。
強度低減
.
特に、これはJITが置き換えられないことを意味します。
整数分割
と
乗算
. (参照
なぜGCCは整数の除算を実装する際に奇妙な数による乗算を使うのですか?
というのは、コンパイラが最適化されている場合、インライン化/定数伝搬の後にコンパイル時定数を指定すると、asmのトリックを使うことができるからです。 整数リテラルは
%
式でも最適化されます。
gcc -O0
OSRスタブでもJITerによって最適化されるのと同じです)。
しかし、同じメソッドを複数回実行すると、2回目以降は完全に最適化された通常の(OSRでない)コードが実行されます。この理論を証明するベンチマークを紹介します ( JMHを用いたベンチマーク ):
@State(Scope.Benchmark)
public class Div {
@Benchmark
public void divConst(Blackhole blackhole) {
long startNum = 0;
long stopNum = 100000000L;
for (long i = startNum; i <= stopNum; i++) {
if (i % 50000 == 0) {
blackhole.consume(i);
}
}
}
@Benchmark
public void divVar(Blackhole blackhole) {
long startNum = 0;
long stopNum = 100000000L;
long progressCheck = 50000;
for (long i = startNum; i <= stopNum; i++) {
if (i % progressCheck == 0) {
blackhole.consume(i);
}
}
}
}
そして、その結果。
# Benchmark: bench.Div.divConst
# Run progress: 0,00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration 1: 126,967 ms/op
# Warmup Iteration 2: 105,660 ms/op
# Warmup Iteration 3: 106,205 ms/op
Iteration 1: 105,620 ms/op
Iteration 2: 105,789 ms/op
Iteration 3: 105,915 ms/op
Iteration 4: 105,629 ms/op
Iteration 5: 105,632 ms/op
# Benchmark: bench.Div.divVar
# Run progress: 50,00% complete, ETA 00:00:09
# Fork: 1 of 1
# Warmup Iteration 1: 844,708 ms/op <-- much slower!
# Warmup Iteration 2: 105,893 ms/op <-- as fast as divConst
# Warmup Iteration 3: 105,601 ms/op
Iteration 1: 105,570 ms/op
Iteration 2: 105,475 ms/op
Iteration 3: 105,702 ms/op
Iteration 4: 105,535 ms/op
Iteration 5: 105,766 ms/op
の最初の繰り返しは
divVar
は、非効率的にコンパイルされたOSRスタブのために、確かにかなり遅いです。しかし、このメソッドが最初から再実行されるとすぐに、利用可能なすべてのコンパイラ最適化を活用した、新しい無制約バージョンが実行されます。
関連
-
jd-gui Java Exceptionが発生しました。
-
をインスタンス化することができません。
-
[解決済み] この2回(1927年)を引き算すると、なぜおかしな結果になるのでしょうか?
-
[解決済み] なぜパスワードにはStringではなくchar[]が好まれるのですか?
-
[解決済み] serialVersionUIDとは何ですか、またなぜそれを使用する必要がありますか?
-
[解決済み] B "の印刷が "#"の印刷より劇的に遅いのはなぜですか?
-
[解決済み] Python 3で「1000000000000000 in range(1000000000000001)」はなぜ速いのですか?
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
-
[解決済み】なぜJavaの+=, -=, *=, /=複合代入演算子はキャスティングを必要としないのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java エラー報告 スレッド "main" での例外 java.util.NoSuchElementException
-
Java のエラーです。未解決のコンパイル問題 解決方法
-
Dateが型に解決できない問題を解決する
-
コンストラクタの呼び出しは、コンストラクタのエラー理解の最初のステートメントである必要があります。
-
Methodのinvokeメソッド実装のJavaリフレクション
-
linux run jarfile Invalid or corrupt jarfile error.
-
java 例外。Javaツールの初期化
-
IDEA パッケージステートメントの欠落
-
Google Chromeのエラー「Not allowed to load local resource」の解決策について
-
[解決済み] Javaで正しいマイクロベンチマークを書くには?