[英]Java - calling static methods vs manual inlining - performance overhead
我感興趣的是我是否應該手動內聯在一些性能敏感算法中稱為100k-100萬次的小方法。
首先,我認為,由於沒有內聯,我會產生一些開銷,因為JVM必須確定是否要內聯這個方法(甚至不能這樣做)。
然而,前幾天,我用靜態方法的調用替換了這個手動內聯代碼,並看到了性能提升。 怎么可能? 這是否表明實際上沒有開銷,讓JVM內聯“意志”實際上提升了性能? 或者這在很大程度上取決於平台/架構?
(發生性能提升的示例是使用靜態方法調用swap(int[] a, int i, int j)
)替換數組交換( int t = a[i]; a[i] = a[j]; a[j] = t;
)) swap(int[] a, int i, int j)
。另一個沒有性能差異的例子是我內聯10個方法,稱為1000000次。)
我見過類似的東西。 “手動內聯”不一定更快,結果程序可能太復雜而無法進行優化分析。
在你的例子中,讓我們做一些瘋狂的猜測。 當您使用swap()方法時,JVM可能能夠分析方法體,並得出結論,由於i和j不會更改,盡管有4個數組訪問,但只需要2個范圍檢查而不是4個。局部變量t
是沒有必要的,JVM可使用2個寄存器來完成這項工作,而不涉及R / W的t
上堆疊。
之后,swap()的主體被內聯到調用方法中。 這是在上一次優化之后,因此保存仍然存在。 調用者方法體甚至可能已經證明i和j總是在范圍內,因此剩下的2個范圍檢查也被丟棄。
現在在手動內聯版本中,優化器必須立即分析整個程序,變量太多,動作太多,可能無法證明保存范圍檢查或消除局部變量t
。 在最壞的情況下,這個版本可能需要花費6個以上的內存訪問來進行交換,這是一個巨大的開銷。 即使只有1個額外的內存讀取,它仍然非常明顯。
當然,我們沒有理由認為手動“概述”總是更好,即提取小方法,如願以為它會幫助優化器。
-
我所學到的是,忘記手動微優化。 並不是我不關心微觀性能改進,而是我始終信任JVM的優化。 這是我完全不知道該做什么比做壞事更好。 所以我放棄了。
JVM可以非常有效地內聯小方法。 唯一能夠自我介紹的好處是,如果你可以刪除代碼,即通過內聯來簡化代碼。
JVM在識別這些結構時會查找某些結構並進行一些“手動編碼”優化。 通過使用交換方法,JVM可以識別結構並通過特定優化以不同方式對其進行優化。
您可能有興趣嘗試OpenJDK 7調試版本,該版本可以選擇打印出它生成的本機代碼。
對不起我遲到的回復,但我剛發現這個話題,引起了我的注意。
在Java中開發時,嘗試編寫“簡單而愚蠢”的代碼。 原因:
如果方法是手動內聯的,那么它只是編譯器首先嘗試理解的另一種方法的一部分,並且看是否有時間將其轉換為二進制代碼,或者是否必須稍等一下才能理解程序流程。 此外,根據方法的作用,在運行期間可以進行多次重新JIT:> JVM僅在“預熱”后生成最佳二進制代碼...並且可能在JVM自行升溫之前程序結束(因為我期望最終表現應該非常相似)。
結論:在C / C ++中優化代碼是有意義的(因為二進制轉換是靜態的),但相同的優化通常不會對Java產生影響,因為編譯器JIT是字節代碼,而不是源代碼。 順便說一句,從我看到的javac甚至懶得做出優化:)
然而,前幾天,我用靜態方法的調用替換了這個手動內聯代碼,並看到了性能提升。 怎么可能?
可能JVM分析器在一個地方(靜態方法)比在單獨實施多次時更容易看到瓶頸。
Hotspot JIT編譯器能夠內聯很多東西,特別是在-server
模式下,雖然我不知道你是如何得到實際的性能提升的。 (我的猜測是內聯是通過方法調用計數完成的,並且交換這兩個值的方法不會經常調用。)
順便說一句,如果它的性能真的很重要,你可以嘗試這個來交換兩個int
值。 (我不是說它會更快,但它可能值得一試。)
a[i] = a[i] ^ a[j];
a[j] = a[i] ^ a[j];
a[i] = a[i] ^ a[j];
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.