第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么 if (variable1 % variable2 == 0) 效率低下?

為什么 if (variable1 % variable2 == 0) 效率低下?

有只小跳蛙 2022-05-25 16:45:20
我是java新手,昨晚正在運行一些代碼,這真的讓我很困擾。variable % variable我正在構建一個簡單的程序來在 for 循環(huán)中顯示每個 X 輸出,當我使用模數(shù)作為vsvariable % 5000或諸如此類時,我注意到性能大幅下降。有人可以向我解釋為什么會這樣以及是什么原因造成的嗎?所以我可以變得更好...這是“高效”的代碼(對不起,如果我有一些語法錯誤,我現(xiàn)在不在電腦上使用代碼)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);    }}請注意,我有一個日期變量來測量差異,一旦它變得足夠長,第一個需要 50 毫秒,而另一個需要 12 秒或類似的時間。如果您的 PC 比我的效率更高,您可能需要增加stopNum或減少。progressCheck我在網(wǎng)上尋找這個問題,但我找不到答案,也許我只是問得不對。編輯:我沒想到我的問題如此受歡迎,我感謝所有答案。我確實在每一半所花費的時間上執(zhí)行了一個基準測試,效率低下的代碼花費了相當長的時間,1/4 秒與 10 秒的給予或接受。當然,他們正在使用 println,但他們都在做相同的數(shù)量,所以我不認為這會產(chǎn)生太大的偏差,特別是因為差異是可重復的。至于答案,由于我是 Java 新手,我現(xiàn)在讓投票決定哪個答案是最好的。我會盡量在星期三之前挑一個。EDIT2:今晚我將進行另一次測試,它不是模數(shù),而是增加一個變量,當它到達progressCheck時,它將執(zhí)行一個,然后將該變量重置為0。對于第三個選項。EDIT3.5:我使用了這段代碼,下面我將展示我的結(jié)果。謝謝大家的幫助!我還嘗試將 long 的 short 值與 0 進行比較,因此我所有的新檢查都會發(fā)生“65536”次,使其重復相等。結(jié)果:固定 = 874 毫秒(通常在 1000 毫秒左右,但由于它是 2 的冪而更快)變量 = 8590 毫秒最終變量 = 1944 毫秒(使用 50000 時約為 1000 毫秒)增量 = 1904 毫秒短轉(zhuǎn)換 = 679 毫秒不足為奇的是,由于缺乏劃分,短轉(zhuǎn)換比“快速”方式快 23%。這很有趣。如果您需要每 256 次(或大約在那里)顯示或比較一些東西,您可以這樣做,并使用if ((byte)integer == 0) {'Perform progress check code here'}最后一個有趣的說明,在“最終聲明的變量”上使用 65536(不是一個漂亮的數(shù)字)的模數(shù)是(慢)固定值速度的一半。之前它以接近相同的速度進行基準測試。
查看完整描述

3 回答

?
手掌心

TA貢獻1942條經(jīng)驗 獲得超3個贊

您正在測量OSR(堆棧上替換)存根。


OSR 存根是一種特殊版本的編譯方法,專門用于在方法運行時將執(zhí)行從解釋模式轉(zhuǎn)移到編譯代碼。


OSR 存根不像常規(guī)方法那樣優(yōu)化,因為它們需要與解釋幀兼容的幀布局。我已經(jīng)在以下答案中展示了這一點:1 , 2 , 3。


類似的事情也發(fā)生在這里。當“低效代碼”運行一個長循環(huán)時,該方法是專門為循環(huán)內(nèi)的堆棧替換而編譯的。狀態(tài)從解釋幀轉(zhuǎn)移到 OSR 編譯方法,該狀態(tài)包括progressCheck局部變量。此時 JIT 無法用常量替換變量,因此無法應用某些優(yōu)化,如強度降低。


特別是這意味著 JIT 不會用乘法代替整數(shù)除法。(請參閱為什么 GCC 在實現(xiàn)整數(shù)除法時使用乘以一個奇怪的數(shù)字?對于提前編譯器的 asm 技巧,當值是內(nèi)聯(lián)/常量傳播后的編譯時常量時,如果啟用了這些優(yōu)化.表達式中的整數(shù)文字也通過 優(yōu)化,類似于此處由 JITer 優(yōu)化的地方,即使在 OSR 存根中也是如此。)%gcc -O0


但是,如果您多次運行相同的方法,則第二次和后續(xù)運行將執(zhí)行常規(guī)(非 OSR)代碼,這是完全優(yōu)化的。這是證明理論的基準(使用 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);

            }

        }

    }

}

結(jié)果:


# 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

由于 OSR 存根編譯效率低下,第一次迭代divVar確實慢得多。但是,只要方法從頭開始重新運行,就會執(zhí)行新的不受約束的版本,該版本會利用所有可用的編譯器優(yōu)化。


查看完整回答
反對 回復 2022-05-25
?
ITMISS

TA貢獻1871條經(jīng)驗 獲得超8個贊

在跟進@phuclv comment時,我檢查了JIT 1生成的代碼,結(jié)果如下:


對于variable % 5000(除以常數(shù)):


mov     rax,29f16b11c6d1e109h

imul    rbx

mov     r10,rbx

sar     r10,3fh

sar     rdx,0dh

sub     rdx,r10

imul    r10,rdx,0c350h    ; <-- imul

mov     r11,rbx

sub     r11,r10

test    r11,r11

jne     1d707ad14a0h

對于variable % variable:


mov     rax,r14

mov     rdx,8000000000000000h

cmp     rax,rdx

jne     22ccce218edh

xor     edx,edx

cmp     rbx,0ffffffffffffffffh

je      22ccce218f2h

cqo

idiv    rax,rbx           ; <-- idiv

test    rdx,rdx

jne     22ccce218c0h

因為除法總是比乘法花費更長的時間,所以最后一個代碼片段的性能較低。


爪哇版:


java version "11" 2018-09-25

Java(TM) SE Runtime Environment 18.9 (build 11+28)

Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)


查看完整回答
反對 回復 2022-05-25
?
子衿沉夜

TA貢獻1828條經(jīng)驗 獲得超3個贊

正如其他人所指出的,一般的模運算需要進行除法。在某些情況下,除法可以(由編譯器)用乘法代替。但與加法/減法相比,兩者都可能很慢。因此,可以通過以下方式獲得最佳性能:


long progressCheck = 50000;


long counter = progressCheck;


for (long i = startNum; i <= stopNum; i++){

    if (--counter == 0) {

        System.out.println(i);

        counter = progressCheck;

    }

}

(作為一個小的優(yōu)化嘗試,我們在這里使用一個預遞減遞減計數(shù)器,因為在許多架構上0,與算術運算之后的立即比較成本正好為 0 指令/CPU 周期,因為 ALU 的標志已經(jīng)由前面的操作適當?shù)卦O置。一個體面的優(yōu)化但是,即使您編寫了 .,編譯器也會自動進行優(yōu)化if (counter++ == 50000) { ... counter = 0; }。)


i請注意,您通常并不真正想要/需要模數(shù),因為您知道循環(huán)計數(shù)器(如果加一計數(shù)器達到某個值。


另一個“技巧”是使用二次冪值/限制,例如progressCheck = 1024;. 模數(shù) 2 的冪可以通過按位快速計算and,即if ( (i & (1024-1)) == 0 ) {...}. 這也應該很快,并且在某些架構上可能會優(yōu)于上面的顯式counter。


查看完整回答
反對 回復 2022-05-25
  • 3 回答
  • 0 關注
  • 113 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網(wǎng)微信公眾號