Hikari连接池FastList与ArrayList的对比

最近在给项目换连接池的时候,偶然看到有文章说hikari的FastList做了一些优化,比ArrayList效率要好,于是研究了一下。

这是官方的介绍https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole

ArrayList
One non-trivial (performance-wise) optimization was eliminating the use of an ArrayList 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.

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 was replaced with a custom class FastList which eliminates range checking and performs removal scans from tail to head.

机翻一下

数组列表
一项重要的(性能方面的)优化是消除用于跟踪打开实例的ArrayList实例的ConnectionProxy使用Statement。当 aStatement关闭时,它必须从这个集合中移除,当Connection它关闭时,它必须迭代集合并关闭所有打开的Statement实例,最后必须清除集合。JavaArrayList明智地用于通用用途,在每次调用时执行范围检查get(int index)。然而,因为我们可以提供关于我们的范围的保证,所以这个检查仅仅是开销。

此外,该remove(Object)实现执行从头到尾的扫描,但是 JDBC 编程中的常见模式是在使用后立即关闭Statements,或以打开的相反顺序。对于这些情况,从尾部开始的扫描性能会更好。因此,ArrayList被替换为自定义类FastList,该类 消除了范围检查并执行从尾部到头部的移除扫描。

这个文档的作者Brett Wooldridge也正是FastList类的编写者。按照上面的说法,主要优化在于取消了范围检查,以及删除时从后向前进行遍历。这究竟能提高多少效率,我们还是来看下源码吧。

根据git上的介绍https://github.com/brettwooldridge/HikariCP,添加maven依赖。

<dependency>
   <groupId>com.zaxxer</groupId>
   <artifactId>HikariCP</artifactId>
   <version>4.0.3</version>
</dependency>

如果用的不是java8,需要按照文档更换依赖。

get方法对比

FastList

   /**
    * Get the element at the specified index.
    *
    * @param index the index of the element to get
    * @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid
    */
   @Override
   public T get(int index)
   {
      return elementData[index];
   }

ArrayList

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    /**
     * Checks if the given index is in range.  If not, throws an appropriate
     * runtime exception.  This method does *not* check if the index is
     * negative: It is always used immediately prior to an array access,
     * which throws an ArrayIndexOutOfBoundsException if index is negative.
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

唯一的区别就是删去了rangeCheck方法,这是用来验证数组下标越界的。按照文档里的说法,hikari连接池应该会以其他方式确保不发送下标越界,所以不需要这种通用性的功能。

就效率而言,这种优化带来的影响应该是非常小的,尤其是对于数据库查询来说,基本是可以忽略不计的。

remove方法对比

FastList

   /**
    * This remove method is most efficient when the element being removed
    * is the last element.  Equality is identity based, not equals() based.
    * Only the first matching element is removed.
    *
    * @param element the element to remove
    */
   @Override
   public boolean remove(Object element)
   {
      for (int index = size - 1; index >= 0; index--) {
         if (element == elementData[index]) {
            final int numMoved = size - index - 1;
            if (numMoved > 0) {
               System.arraycopy(elementData, index + 1, elementData, index, numMoved);
            }
            elementData[--size] = null;
            return true;
         }
      }

      return false;
   }

ArrayList

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

源码同样很简单就不解释了。这真的就只是换了一下遍历的顺序,还有一个区别是equals换成了==。关于第二个不同,我没有完整阅读过整个框架的源码,猜测是这个list只存放Statement,所以不需要equals吧。

从效率上来说,这种优化带来的影响同样是非常小的。理论上来说,在特定应用场景下,会显著降低遍历的次数,但是ArrayList的遍历本来就是非常高效的,这相对于数据库查询的时间来说也是可以忽略不计的。

最后,我又再看了一下文档,发现上面有这样一句

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.

原来作者自己也说了,这些优化带来的影响是非常小的。

本来我是想看看是不是有什么创新性的方法出现的,看完源码后还是有点失望。FastList并没有显著的改善ArrayList的效率,它只是把针对自己特定的使用场景进行了非常微小的改进。不过这种最求完美的精神还是值得我们学习的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值