Down the Rabbit Hole

Down the Rabbit Hole

?? Micro-optimizations 小優(yōu)化

HikariCP包含很多獨立的微小的優(yōu)化绒瘦,這些優(yōu)化幾乎都無法評估測量讲仰,但是所有小優(yōu)化一起形成了一個整體的性能提升楞慈。其中的一些優(yōu)化是以幾毫秒平攤在數以百萬計的調用膊存。

HikariCP contains many micro-optimizations that individually are barely measurable, but together combine as a boost to overall performance. Some of these optimizations are measured in fractions of a millisecond amortized over millions of invocations.

ArrayList

One non-trivial (performance-wise) optimization was eliminating the use of an ArrayList<Statement> instance in the ConnectionProxy used to track open Statement instances. When a Statement is closed, it must be removed from this collection, and when the Connection is closed it must iterate the collection and close any open Statement instances, and finally must clear the collection. The Java ArrayList, wisely for general purpose use, performs a range check upon every get(int index) call. However, because we can provide guarantees about our ranges, this check is merely overhead.

其中一個有意義的優(yōu)化就是消除在ConnectionProxy中用于追蹤活躍的Statement對象的ArrayList<Statement> 赘淮。當Statement關閉了岔帽,它必須從集合中刪除荷荤,并且當整個Connection關閉了退渗,他必須迭代集合然后關閉所有的Statement實例移稳,最終清理整個集合對象。java的ArrayList為了通常的使用会油,在每個get(int index)調用時都做了越界檢查秒裕,但是因為在這里我們可以確保索引范圍,所有這個檢查是多余的支出钞啸。

Additionally, the remove(Object) implementation performs a scan from head to tail, however common patterns in JDBC programming are to close Statements immediately after use, or in reverse order of opening. For these cases, a scan that starts at the tail will perform better. Therefore, ArrayList<Statement> was replaced with a custom class FastList which eliminates range checking and performs removal scans from tail to head.

另外remove(Object) 的實現是從頭到尾掃描數組几蜻,然而通常的JDBC編程模型是在使用了Statements之后立即關閉(跟創(chuàng)建statement順序相反)。因此如果從尾部開始掃描性能會更加好体斩。因此ArrayList<Statement>由我們自己定義的一個類FastList來代替掉了梭稚,它消除了索引越界的檢查以及在刪除時執(zhí)行從尾到頭部的掃描操作。

ConcurrentBag

HikariCP contains a custom lock-free collection called a ConcurrentBag. The idea was borrowed from the C# .NET ConcurrentBag class, but the internal implementation quite different. The ConcurrentBag provides...

  • A lock-free design 無鎖設計

  • ThreadLocal caching 本地線程緩存

  • Queue-stealing 工作竊取隊列

  • Direct hand-off optimizations

...resulting in a high degree of concurrency, extremely low latency, and minimized occurrences of false-sharing.

Invocation: invokevirtual vs invokestatic

簡而言之:原先的是單例工廠方法絮吵,改成類的靜態(tài)方法弧烤。即原先調用獲取代理連接等等的流程是 getStatic 獲取單例的工廠對象,然后invokeVirtual調用對象的具體方法來返回代理類蹬敲。后面改進之后直接invokeStatic調用類的靜態(tài)方法來返回代理對象暇昂,因此性能得到提升。

In order to generate proxies for Connection, Statement, and ResultSet instances HikariCP was initially using a singleton factory, held in the case of ConnectionProxy in a static field (PROXY_FACTORY).

為了生成Connection, Statement, and ResultSet 的代理對象實例伴嗡,HikariCP最開始使用了單例工廠方法急波,在ConnectionProxy對象的一個靜態(tài)屬性(PROXY_FACTORY)中。

There was a dozen or so methods resembling the following:

項目中有許多如下類似的方法:

public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
{
    return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
}

Using the original singleton factory, the generated bytecode looked like this:

    public final java.sql.PreparedStatement prepareStatement(java.lang.String, java.lang.String[]) throws java.sql.SQLException;
    flags: ACC_PRIVATE, ACC_FINAL
    Code:
      stack=5, locals=3, args_size=3
         0: getstatic     #59                 // Field PROXY_FACTORY:Lcom/zaxxer/hikari/proxy/ProxyFactory;
         3: aload_0
         4: aload_0
         5: getfield      #3                  // Field delegate:Ljava/sql/Connection;
         8: aload_1
         9: aload_2
        10: invokeinterface #74,  3           // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/PreparedStatement;
        15: invokevirtual #69                 // Method com/zaxxer/hikari/proxy/ProxyFactory.getProxyPreparedStatement:(Lcom/zaxxer/hikari/proxy/ConnectionProxy;Ljava/sql/PreparedStatement;)Ljava/sql/PreparedStatement;
        18: return

You can see that first there is a getstatic call to get the value of the static field PROXY_FACTORY, as well as (lastly) the invokevirtual call to getProxyPreparedStatement() on the ProxyFactory instance.

We eliminated the singleton factory (which was generated by Javassist) and replaced it with a final class having static methods (whose bodies are generated by Javassist). The Java code became:

    public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
    {
        return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
    }

Where getProxyPreparedStatement() is a static method defined in the ProxyFactory class. The resulting bytecode is:

    private final java.sql.PreparedStatement prepareStatement(java.lang.String, java.lang.String[]) throws java.sql.SQLException;
    flags: ACC_PRIVATE, ACC_FINAL
    Code:
      stack=4, locals=3, args_size=3
         0: aload_0
         1: aload_0
         2: getfield      #3                  // Field delegate:Ljava/sql/Connection;
         5: aload_1
         6: aload_2
         7: invokeinterface #72,  3           // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/PreparedStatement;
        12: invokestatic  #67                 // Method com/zaxxer/hikari/proxy/ProxyFactory.getProxyPreparedStatement:(Lcom/zaxxer/hikari/proxy/ConnectionProxy;Ljava/sql/PreparedStatement;)Ljava/sql/PreparedStatement;
        15: areturn

There are three things of note here:

  • The getstatic call is gone.

    get static指令消失了

  • The invokevirtual call is replaced with a invokestatic call that is more easily optimized by the JVM.

    invokevirtual被替換成了invokestatic調用瘪校,更加容易被JVM優(yōu)化澄暮。(invokevirtual需要查詢虛方法表來確定方法的直接引用,invokestatic在類加載的解析階段就從符號引用轉成了直接引用 )

  • Lastly, possibly not noticed at first glance is that the stack size is reduced from 5 elements to 4 elements. This is because in the case of invokevirtual there is an implicit passing of the instance of ProxyFactory on the stack (i.e this), and there is an additional (unseen) pop of that value from the stack when getProxyPreparedStatement() was called.

    方法stack的深度從5變成了4.這是因為先前的invokevirtual調用時需要在stack頂部Pop出一個ProxyFactory實例引用

In all, this change removed a static field access, a push and pop from the stack, and made the invocation easier for the JIT to optimize because the callsite is guaranteed not to change.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末阱扬,一起剝皮案震驚了整個濱河市泣懊,隨后出現的幾起案子,更是在濱河造成了極大的恐慌麻惶,老刑警劉巖馍刮,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異窃蹋,居然都是意外死亡卡啰,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門脐彩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碎乃,“玉大人姊扔,你說我怎么就攤上這事惠奸。” “怎么了恰梢?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵佛南,是天一觀的道長梗掰。 經常有香客問我,道長嗅回,這世上最難降的妖魔是什么及穗? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮绵载,結果婚禮上埂陆,老公的妹妹穿的比我還像新娘。我一直安慰自己娃豹,他們只是感情好焚虱,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著懂版,像睡著了一般鹃栽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躯畴,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天民鼓,我揣著相機與錄音,去河邊找鬼蓬抄。 笑死丰嘉,一個胖子當著我的面吹牛,可吹牛的內容都是我干的嚷缭。 我是一名探鬼主播供嚎,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼峭状!你這毒婦竟也來了克滴?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤优床,失蹤者是張志新(化名)和其女友劉穎劝赔,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體胆敞,經...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡着帽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了移层。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仍翰。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖观话,靈堂內的尸體忽然破棺而出予借,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布灵迫,位于F島的核電站秦叛,受9級特大地震影響,放射性物質發(fā)生泄漏瀑粥。R本人自食惡果不足惜挣跋,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狞换。 院中可真熱鬧避咆,春花似錦、人聲如沸修噪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膨报。三九已至,卻和暖如春适荣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弛矛。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工够吩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丈氓。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓周循,卻偏偏與公主長得像,于是被迫代替她去往敵國和親万俗。 傳聞我的和親對象是個殘疾皇子湾笛,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內容