- 使用了 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)的程序員,都知道把常量放在前面弥虐,避免空指針
-
- obj1.equals(obj2)
這樣使用扩灯,會(huì)有
Java.lang.NullPointerException
的風(fēng)險(xiǎn) ,所以常規(guī)霜瘪,會(huì)有一個(gè)if(null != str1){}
作為前提驴剔。
-
-
阿里將其包裝了一下:
- 好處1:寫成一個(gè)工具類,直接使用即可粥庄,避免新人犯錯(cuò)誤的成本丧失。
- 好處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)明出處。
有問題锦聊,可以給我留言歹嘹。