JMH-分析equals的優(yōu)化空間

  • 使用了 JMH之后楣号,對(duì)于每一個(gè)小方法最易,都可以做一個(gè)非常深入的研究怒坯,對(duì)比,不一樣的寫法藻懒,不一樣的風(fēng)格剔猿,到底有沒有區(qū)別,到底誰最好嬉荆。

本次深入研究一下 equals

  • 代碼來源是 查看 阿里開源包中的工具包中的代碼归敬,看到之后,眼前一亮鄙早,然后進(jìn)入了深思汪茧。

     /**
         * check whether two components are equal<br/>
         *
         * @param src    source component
         * @param target target component
         * @param <E>    component type
         * @return <br/>
         * (null, null)    == true
         * (1L,2L)         == false
         * (1L,1L)         == true
         * ("abc",null)    == false
         * (null,"abc")    == false
         */
        public static <E> boolean isEquals(E src, E target) {
            return null == src
                    && null == target
                    || null != src
                    && null != target
                    && src.equals(target);
        }
    
  • 對(duì)于 equals的使用,常規(guī)會(huì)有兩種方式:

    • 1) 常量.equals(obj)

      這判斷限番,沒有問題舱污,有些經(jīng)驗(yàn)的程序員,都知道把常量放在前面弥虐,避免空指針

      1. obj1.equals(obj2)

      這樣使用扩灯,會(huì)有 Java.lang.NullPointerException 的風(fēng)險(xiǎn) ,所以常規(guī)霜瘪,會(huì)有一個(gè) if(null != str1){}作為前提驴剔。

  • 阿里將其包裝了一下:

      1. 好處1:寫成一個(gè)工具類,直接使用即可粥庄,避免新人犯錯(cuò)誤的成本丧失。
      1. 好處2:這個(gè)工具類,包裹住 非空的判斷惜互, 讓使用的代碼布讹,簡(jiǎn)潔。
    • 3)疑問:這樣包裝训堆,是否會(huì)有性能的提升空間描验?

下面開始進(jìn)行性能的詳細(xì)分析

1)以往常規(guī)的測(cè)試方法,會(huì)寫一個(gè) main方法坑鱼,進(jìn)行測(cè)試:
  public static void main(String[] args) {
        int[] count = {10000,100000,1000000,10000000};
        for(int num : count){
            System.out.println("------------執(zhí)行"+num+"次------------");
            testEqu(num);
        }
    }
    public static void testEqu(int count){
        Affect affect = new Affect();
        String a = "1";
        String b = "2";
        int num = count;
        int temp = 0;
        while (num-- > 0) {
            if (a != null && a.equals(b)) {
                temp++;
            }
            temp++;
        }
        System.out.println("  a.equals(b):"+affect.cost());
        affect = new Affect();
        num = count;
        temp = 0;
        while (num-- > 0) {
            if (isEquals(a, b)) {
                temp++;
            }
            temp++;
        }
        System.out.println("isEquals(a, b):"+affect.cost());
    }

運(yùn)行結(jié)果:

------------執(zhí)行10000次------------
  a.equals(b):1
isEquals(a, b):0
------------執(zhí)行100000次------------
  a.equals(b):5
isEquals(a, b):2
------------執(zhí)行1000000次------------
  a.equals(b):3
isEquals(a, b):18
------------執(zhí)行10000000次------------
  a.equals(b):29
isEquals(a, b):28

看到這個(gè)結(jié)果膘流,小伙子們,是不是會(huì)覺得非常奇怪鲁沥,為什么呢呼股?

看到這個(gè)結(jié)果,小伙子們画恰,是不是會(huì)覺得非常奇怪彭谁,為什么呢?

看到這個(gè)結(jié)果允扇,小伙子們缠局,是不是會(huì)覺得非常奇怪则奥,為什么呢?

  • 然后我們來改造一下代碼:
public static void main(String[] args) throws InterruptedException {
        int[] count = {10000,100000,1000000,10000000};
        for(int num : count){
            System.out.println("------------執(zhí)行"+num+"次------------");
            testEqu(num);
            Thread.sleep(2000); // -------------------------加了這里
        }
    }
    public static void testEqu(int count) throws InterruptedException {
        Affect affect = new Affect();
        String a = "1";
        String b = "2";
        int num = count;
        int temp = 0;
        while (num-- > 0) {
            if (a != null && a.equals(b)) {
                temp++;
            }
            temp++;
        }
        System.out.println("  a.equals(b):"+affect.cost());
        Thread.sleep(2000);// -------------------------加了這里
        affect = new Affect();
        num = count;
        temp = 0;
        while (num-- > 0) {
            if (isEquals(a, b)) {
                temp++;
            }
            temp++;
        }
        System.out.println("isEquals(a, b):"+affect.cost());
    }

結(jié)果是:

------------執(zhí)行10000次------------
  a.equals(b):0
isEquals(a, b):0
------------執(zhí)行100000次------------
  a.equals(b):7
isEquals(a, b):2
------------執(zhí)行1000000次------------
  a.equals(b):17
isEquals(a, b):3
------------執(zhí)行10000000次------------
  a.equals(b):45
isEquals(a, b):34

這樣看狭园,就比較明白了读处,但是這是為什么呢?

  • 最主要的原因是GC的垃圾回收導(dǎo)致唱矛,這樣寫測(cè)試代碼档泽,是不準(zhǔn)確的,變量都在一個(gè)類里揖赴,當(dāng)對(duì)象的生命周期結(jié)束了馆匿,GC的過程,會(huì)影響其他代碼的運(yùn)作燥滑。
  • 還有一個(gè)問題渐北,就是 JIT , 運(yùn)行次數(shù)沒有到一定的程度铭拧,無法進(jìn)入 JIT赃蛛,但是靜態(tài)方法塊,先天就有優(yōu)勢(shì)搀菩,提前進(jìn)入了JIT呕臂,所以也有可能不準(zhǔn)確

so ,引出了 JMH 肪跋, 見下面

2)使用 JMH 看一下情況

代碼如下:

package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import static org.openjdk.jmh.samples.ArthasCheckUtils.isEquals;

@State(Scope.Thread) // 每個(gè)測(cè)試線程一個(gè)實(shí)例
public class JMHDemo01 {
    @Benchmark
    public String stringConcat() {
        String a = "1";
        String b = "2";
        int f = 0;
        if (a != null) {
            if (a.equals(b)) {
                f++;
            }
        }
        return "";
    }
    @Benchmark
    public String stringConcatIsEquals() {
        String a = "1";
        String b = "2";
        int f = 0;
        if(isEquals(a, b)) {
            f++;
        }
        return "";
    }
}

測(cè)試 main 方法

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(JMHDemo01.class.getSimpleName())
            .forks(1)
            .build();

    new Runner(opt).run();
}

看看結(jié)果:

Benchmark                        Mode  Cnt          Score        Error  Units
JMHDemo01.stringConcat          thrpt    5  132834643.560 ± 443289.509  ops/s
JMHDemo01.stringConcatIsEquals  thrpt    5  132995301.389 ±  52712.796  ops/s

  • 結(jié)論:

    • 使用了JMH 之后歧蒋, 發(fā)現(xiàn) 結(jié)果非常的接近,包裝了 equals 之后州既,性能還是提高了滿多的谜洽,十幾萬次ops, 但是沒有之前的測(cè)試的這么大的差距吴叶,只相差 0.12%.

    • 確定了 之前 考慮的 JIT的問題阐虚,他們其實(shí)是一樣的方法,一樣都到熱區(qū)后蚌卤,理論上的性能实束,應(yīng)該是一致的。

    • 但是對(duì)于這樣優(yōu)化的必要性而言逊彭,還是非常有必要的咸灿,其他的優(yōu)勢(shì)依舊存在:

      • 1)代碼整潔美觀的提升
      • 2)編碼質(zhì)量的提升
      • 3)提前進(jìn)入熱區(qū)
      • 4)可以從結(jié)果中看出,優(yōu)化后的方法诫龙,更加穩(wěn)定

測(cè)試代碼析显,有寫的不對(duì)的地方,請(qǐng)指出签赃,轉(zhuǎn)載谷异,請(qǐng)標(biāo)明出處。

有問題锦聊,可以給我留言歹嘹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孔庭,隨后出現(xiàn)的幾起案子尺上,更是在濱河造成了極大的恐慌,老刑警劉巖圆到,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怎抛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡芽淡,警方通過查閱死者的電腦和手機(jī)马绝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挣菲,“玉大人富稻,你說我怎么就攤上這事“渍停” “怎么了椭赋?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)或杠。 經(jīng)常有香客問我哪怔,道長(zhǎng),這世上最難降的妖魔是什么向抢? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任蔓涧,我火速辦了婚禮,結(jié)果婚禮上笋额,老公的妹妹穿的比我還像新娘元暴。我一直安慰自己,他們只是感情好兄猩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布茉盏。 她就那樣靜靜地躺著,像睡著了一般枢冤。 火紅的嫁衣襯著肌膚如雪鸠姨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天淹真,我揣著相機(jī)與錄音讶迁,去河邊找鬼。 笑死核蘸,一個(gè)胖子當(dāng)著我的面吹牛巍糯,可吹牛的內(nèi)容都是我干的啸驯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼祟峦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼罚斗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宅楞,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤针姿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后厌衙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體距淫,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年婶希,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榕暇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饲趋,死狀恐怖拐揭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奕塑,我是刑警寧澤堂污,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站龄砰,受9級(jí)特大地震影響盟猖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜换棚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一式镐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧固蚤,春花似錦娘汞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至燎孟,卻和暖如春禽作,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揩页。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工旷偿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓萍程,卻偏偏與公主長(zhǎng)得像幢妄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尘喝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Overriding the equals method seems simple, but there are ...
    MrDcheng閱讀 695評(píng)論 0 0
  • 目錄faster rcnn論文備注caffe代碼框架簡(jiǎn)介faster rcnn代碼分析后記 faster rcnn...
    db24cc閱讀 9,614評(píng)論 2 12
  • js預(yù)解釋是代碼執(zhí)行之前就干了些事磁浇,有兩類預(yù)解釋斋陪,帶var 和帶function朽褪,具體看看 帶var關(guān)鍵字預(yù)解釋a...
    sdcV閱讀 167評(píng)論 0 0
  • #本文參加‘青春’大賽,本人保證本文為本人原創(chuàng)无虚,如有問題則與主辦方無關(guān)缔赠,自愿放棄評(píng)優(yōu)評(píng)獎(jiǎng)資格 學(xué)校:平頂山學(xué)院 ...
    半路友人123閱讀 1,732評(píng)論 0 48
  • 每夜 高考考完后將我的東西從出租屋搬回家的那天,我也是像很多人一樣友题,沒有哭嗤堰,但是我卻因?yàn)閷⑽液芏嘞矚g的書掉進(jìn)了泥潭...
    芒萁的話閱讀 129評(píng)論 0 0