Java中的方法调用有多昂贵
我們都去過那兒。 在查看設(shè)計(jì)不良的代碼的同時(shí),聽聽作者對(duì)人們永遠(yuǎn)不應(yīng)該犧牲性能而不是設(shè)計(jì)的解釋。 而且,您不能說服作者擺脫其500行方法,因?yàn)殒溄臃椒ㄕ{(diào)用會(huì)破壞性能。
好吧,這可能在1996年左右是正確的。 但是自那時(shí)以來, JVM已經(jīng)發(fā)展成為一款了不起的軟件。 找出它的一種方法是開始更深入地研究虛擬機(jī)執(zhí)行的優(yōu)化。 JVM應(yīng)用的技術(shù)庫(kù)非常廣泛,但是讓我們更詳細(xì)地研究其中一種。 即方法內(nèi)聯(lián) 。 通過以下示例最容易解釋:
當(dāng)運(yùn)行此代碼時(shí), JVM將弄清楚它可以用更有效的所謂“內(nèi)聯(lián)”代碼代替:
int sum(int a, int b, int c, int d) {return a + b + c + d; }您必須注意,此優(yōu)化是由虛擬機(jī)而不是由編譯器完成的。 最初做出此決定的原因并不透明。 畢竟-如果您查看上面的示例代碼-為什么在編譯時(shí)可以推遲優(yōu)化以產(chǎn)生更有效的字節(jié)碼? 但是,考慮到其他不太明顯的情況,JVM是執(zhí)行優(yōu)化的最佳位置:
- JVM除了靜態(tài)分析外還配備了運(yùn)行時(shí)數(shù)據(jù)。 在運(yùn)行時(shí),JVM可以根據(jù)最常執(zhí)行的方法,冗余的負(fù)載,何時(shí)安全使用副本傳播等做出更好的決策。
- JVM已獲得有關(guān)基礎(chǔ)體系結(jié)構(gòu)的信息-內(nèi)核數(shù),堆大小和配置,因此可以根據(jù)此信息進(jìn)行最佳選擇。
但是,讓我們?cè)趯?shí)踐中看到這些假設(shè)。 我創(chuàng)建了一個(gè)小型測(cè)試應(yīng)用程序 ,它使用幾種不同的方法將1024個(gè)整數(shù)加在一起。
- 相對(duì)合理的一種,其中實(shí)現(xiàn)只是對(duì)包含1024個(gè)整數(shù)的數(shù)組進(jìn)行迭代,并將結(jié)果求和。 InlineSummarizer.java中提供了此實(shí)現(xiàn)。
- 基于遞歸的分而治之方法。 我采用原始的1024個(gè)元素?cái)?shù)組,然后將其遞歸地分成兩半,因此第一個(gè)遞歸深度為我提供了兩個(gè)512個(gè)元素?cái)?shù)組,第二個(gè)深度為4個(gè)256個(gè)元素?cái)?shù)組,依此類推。 為了將所有1024個(gè)元素加在一起,我引入了1023個(gè)其他方法調(diào)用。 此實(shí)現(xiàn)作為RecursiveSummarizer.java附加。
- 天真的分而治之的方法。 這也劃分了原始的1024個(gè)元素的數(shù)組,但是通過在分開的兩半上調(diào)用其他實(shí)例方法-即我嵌套了sum512(),sum256(),sum128(),…,sum2()調(diào)用,直到我總結(jié)了所有元素。 與遞歸一樣,我在源代碼中介紹了1023個(gè)其他方法調(diào)用。
我有一個(gè)測(cè)試班來運(yùn)行所有這些示例。 最初的結(jié)果來自未優(yōu)化的代碼:
從上面可以看出,內(nèi)聯(lián)代碼是最快的。 而我們引入了1023個(gè)其他方法調(diào)用的方法則要慢25,000ns。 但是此圖像必須加以警告-它是JIT尚未完全優(yōu)化代碼的運(yùn)行快照。 在我2010年中期的MB Pro中,根據(jù)實(shí)施情況,運(yùn)行了200到3000次。
更現(xiàn)實(shí)的結(jié)果如下。 我已經(jīng)運(yùn)行了所有匯總器實(shí)現(xiàn)超過1,000,000次,并丟棄了JIT尚未成功實(shí)現(xiàn)魔術(shù)效果的運(yùn)行。
我們可以看到,即使內(nèi)聯(lián)代碼仍然表現(xiàn)最佳,但迭代方法也以相當(dāng)快的速度飛速發(fā)展。 但是遞歸明顯不同–當(dāng)?shù)椒▋H以20%的開銷關(guān)閉時(shí),RecursiveSummarizer會(huì)花費(fèi)內(nèi)聯(lián)代碼需要完成的340%的時(shí)間。 顯然,這是應(yīng)該注意的事情–使用遞歸時(shí),JVM是無助的,無法內(nèi)聯(lián)方法調(diào)用。 因此,在使用遞歸時(shí)請(qǐng)注意此限制。
除了遞歸-方法開銷幾乎不存在。 源代碼中有1023個(gè)其他方法調(diào)用之間的時(shí)間差僅為205 ns。 請(qǐng)記住,那是我們用于測(cè)量的那納秒(10 ^ -9 s)。 因此,借助JIT,我們可以安全地忽略方法調(diào)用帶來的大部分開銷。 下次當(dāng)您的同事將笨拙的設(shè)計(jì)決策隱藏在通過調(diào)用堆棧彈出效率不高的語句后面時(shí),請(qǐng)讓他首先完成一個(gè)小的JIT崩潰過程 。 如果您希望有能力阻止他將來的荒唐言論,請(qǐng)訂閱我們的RSS或Twitter提要,我們很高興為您提供未來的案例研究。
全面披露:本文所用測(cè)試用例的靈感來自Tomasz Nurkiewicz 博客文章 。
參考: Plumbr Blog博客上的JCG合作伙伴 Nikita Salnikov Tarnovski 用Java進(jìn)行方法調(diào)用的成本是多少 ?
翻譯自: https://www.javacodegeeks.com/2013/02/how-expensive-is-a-method-call-in-java.html
總結(jié)
以上是生活随笔為你收集整理的Java中的方法调用有多昂贵的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国备案查询网站(中国备案查询)
- 下一篇: Linux编程 pdf(linux编程