您好,歡迎來(lái)電子發(fā)燒友網(wǎng)! ,新用戶(hù)?[免費(fèi)注冊(cè)]

您的位置:電子發(fā)燒友網(wǎng)>源碼下載>java源碼下載>

java反射原理簡(jiǎn)單介紹

大小:0.8 MB 人氣: 2017-09-27 需要積分:2

  幾個(gè)星期前,我想讓我的代碼運(yùn)行快1000倍,同時(shí)不改變復(fù)雜度,正如標(biāo)題所說(shuō)的,使用Java反射機(jī)制,可以讓代碼運(yùn)行得更快。

  首先來(lái)解釋一下為什么會(huì)首先使用反射機(jī)制。

  我有一個(gè)接口(表示一個(gè)樹(shù)節(jié)點(diǎn))和一個(gè)實(shí)現(xiàn)這個(gè)接口的大量類(lèi)(100+)。訣竅在于,樹(shù)是異構(gòu)的,每個(gè)節(jié)點(diǎn)類(lèi)型可以有不同數(shù)量的子節(jié)點(diǎn),或者以不同的方式存儲(chǔ)它們。

  我需要讓代碼能夠在這樣的組合樹(shù)上運(yùn)行起來(lái)。簡(jiǎn)單的方法是簡(jiǎn)單地向接口添加一個(gè)children()方法,并在每個(gè)節(jié)點(diǎn)中實(shí)現(xiàn)它。當(dāng)然,這很繁瑣,也很乏味。

  相反,我注意到所有的子節(jié)點(diǎn)都是直接的字段,或者聚集在包含節(jié)點(diǎn)集合的字段中。所以可以用反射的方式寫(xiě)一小段代碼,這也對(duì)每一個(gè)節(jié)點(diǎn)都適用!

  我已經(jīng)在Github上放了一個(gè)簡(jiǎn)化版的代碼。我會(huì)把相關(guān)的部分聯(lián)系起來(lái)。

  初始化代碼

  這是我提出的第一版本代碼:WalkerDemoSlowest.java

  它相當(dāng)簡(jiǎn)單:獲取節(jié)點(diǎn)類(lèi)的方法,過(guò)濾掉那些不是getter的方法,然后只考慮返回節(jié)點(diǎn)或節(jié)點(diǎn)集合。調(diào)用這些方法,并在子節(jié)點(diǎn)上遞歸地調(diào)用walk方法。

  如果我說(shuō)這樣的進(jìn)展很慢,有人會(huì)感到驚訝嗎?

  緩存

  有一個(gè)簡(jiǎn)單的調(diào)整,可以使它更快:使用緩存方法查找。

  下面是緩存版本:WalkerDemoSlow.java

  這和每個(gè)實(shí)現(xiàn)節(jié)點(diǎn)的類(lèi)都是一樣的,創(chuàng)建一個(gè)ClassData對(duì)象來(lái)緩存所有相關(guān)的getter方法,所以只需要查找一次,這會(huì)產(chǎn)生一個(gè)令人滿(mǎn)意的10倍加速。

  LambdaMetafactory 奇跡

  不幸的是,這仍然太慢了。所以我向谷歌尋求幫助,發(fā)現(xiàn)了一個(gè)很有用的StackOverflow社區(qū)。

  有答案建議使用LambdaMetafactory,這是一個(gè)標(biāo)準(zhǔn)的庫(kù)類(lèi),它支持lambda語(yǔ)法調(diào)用。

  細(xì)節(jié)在我看來(lái)有些模糊,但似乎通過(guò)使用這些工具,可以在代碼中“打開(kāi)編譯器”,并優(yōu)化反射機(jī)制來(lái)進(jìn)行本機(jī)調(diào)用。這就是一種假設(shè)。

  這是代碼:walkerdemofast.java

  現(xiàn)在,我的代碼可以做到100倍的加速。然而,在寫(xiě)這篇文章的時(shí)候,想用一些代碼片段來(lái)演示這個(gè)效果,但是沒(méi)有成功。我試著給接口實(shí)現(xiàn)3個(gè)子類(lèi),并使用一些偽方法進(jìn)行過(guò)濾,但還是沒(méi)有效果。第二版和第三版的代碼運(yùn)行速度差不多。

  我重新檢查了原來(lái)的代碼,一切看起來(lái)都很好。在原始代碼中,樹(shù)是通過(guò)解析一些源文件得到的抽象語(yǔ)法樹(shù)(AST)。如果限制了前14個(gè)源文件的輸入,我發(fā)現(xiàn)會(huì)得到不同的結(jié)果。

  這些文件相對(duì)較短(幾乎沒(méi)有10行),語(yǔ)法簡(jiǎn)單。但僅僅有這些,第二和第三版代碼仍會(huì)以同樣的速度運(yùn)行。但是在第15個(gè)文件中進(jìn)行輸入(少于100行),那么第二個(gè)版本的代碼會(huì)花費(fèi)36秒,而第三個(gè)版本代碼會(huì)在0.2秒內(nèi)完成,這是700倍的差異。

  我的假設(shè)是,如果場(chǎng)景足夠簡(jiǎn)單,優(yōu)化器會(huì)注意到正在運(yùn)行的代碼并選擇離開(kāi)。在更復(fù)雜的情況下,它會(huì)耗盡優(yōu)化預(yù)算,然后回到未優(yōu)化的版本以及糟糕的性能狀態(tài)。但是,優(yōu)化器已經(jīng)足夠靈活,如果有一個(gè)能擊敗它的示例,那似乎是非常成功的。

  LambdaMetafactory 可能性

  我有點(diǎn)好奇LambdaMetafactory會(huì)有什么樣的可能性。在我的示例中,它會(huì)產(chǎn)生奇跡,因?yàn)榉瓷湔{(diào)用比簡(jiǎn)單的緩存查找要昂貴得多。但它是否也能對(duì)常規(guī)代碼進(jìn)行優(yōu)化處理呢?這似乎不太可能讓megamorphic call sites提供幫助,因?yàn)榫幾g的方法必須以某種方式檢索,而查找的成本將使收益相形見(jiàn)絀。

  但是,如何在運(yùn)行組合代碼時(shí)進(jìn)行優(yōu)化呢?可以提供數(shù)據(jù)結(jié)構(gòu),或者為數(shù)據(jù)結(jié)構(gòu)提供解釋器,并使用LambdaMetafactory“編譯”它們。這是否足夠智能呢,可以對(duì)給定數(shù)據(jù)結(jié)構(gòu)的代碼進(jìn)行部分評(píng)估,從而將解釋器轉(zhuǎn)換成等價(jià)的“plain”代碼?

  順便說(shuō)一下,這正是Truffle框架所采用的方法,它在Graal VM上運(yùn)行,所以這個(gè)想法肯定有一定的意義??赡軙簳r(shí)無(wú)法使用當(dāng)前的JVM,因此需要修改GraalVM。

  在任何情況下,都會(huì)盡量使一些功能成為一個(gè)庫(kù),可以在“常規(guī)程序”(非編譯器)中使用。編寫(xiě)簡(jiǎn)單的解釋器通常是解決一些問(wèn)題的最簡(jiǎn)單方法。

非常好我支持^.^

(0) 0%

不好我反對(duì)

(0) 0%

      發(fā)表評(píng)論

      用戶(hù)評(píng)論
      評(píng)價(jià):好評(píng)中評(píng)差評(píng)

      發(fā)表評(píng)論,獲取積分! 請(qǐng)遵守相關(guān)規(guī)定!

      ?