天天看點

JDK 21:圖像性能改進

作者:CodingView

背景

将原始值(例如 int 和 long 原始值)打包/解包到位元組數組中/從位元組數組中解包時,之前使用顯式位移進行轉換,如 ImageInputStreamImpl::readInt 下面的方法所示:

public int readInt() throws IOException {
    if (read(byteBuf, 0, 4) !=  4) {
        throw new EOFException();
    }
 
    if (byteOrder == ByteOrder.BIG_ENDIAN) {
        return
            (((byteBuf[0] & 0xff) << 24) | ((byteBuf[1] & 0xff) << 16) |      // (1)
             ((byteBuf[2] & 0xff) <<  8) | ((byteBuf[3] & 0xff) <<  0));
    } else {
        return
            (((byteBuf[3] & 0xff) << 24) | ((byteBuf[2] & 0xff) << 16) |      // (2)
             ((byteBuf[1] & 0xff) <<  8) | ((byteBuf[0] & 0xff) <<  0));
    }
}           
  1. 通過位移位進行大端解包
  2. 通過位移位進行小端解包

這裡使用的方案與我在上一篇文章中描述的方案類似 ,是以我不會再深入細節。簡而言之,這種方法複雜且具有挑戰性,Java 無法對其進行全面優化。此外,對于我們人類來說,閱讀也很困難。

JDK 21 中的改進

在 Java 21 中,轉換是通過 VarHandle 新的 jdk.internal.util.ByteArray class. 以下是内部 ByteArray 類的部分内容:

private static final VarHandle INT =
        MethodHandles.byteArrayViewVarHandle(int[], ByteOrder.BIG_ENDIAN);
 
 
static int getInt(byte[] b, int off) {
    return (int) INT.get(b, off);
}           

與顯式位移相比,使用 VarHandles 意味着 Java 能夠更好地優化方法。

上面的類處理 big-endian。由于圖像也需要能夠處理 小端,是以ByteArrayLittleEndian添加了 一個名為的新類 。這意味着 readInt() 可以像這樣簡化和改進該方法:

public int readInt() throws IOException {
    if (read(byteBuf, 0, 4) !=  4) {
        throw new EOFException();
    }
 
    return (byteOrder == ByteOrder.BIG_ENDIAN)
            ? ByteArray.getInt(byteBuf, 0)
            : ByteArrayLittleEndian.getInt(byteBuf, 0);
}           

好的!現在看起來幹淨多了。

受影響的類别和影響

直接改進了以下類:

  • ImageInputStreamImpl
  • ImageOutputStreamImpl

好消息是,這些類為包中和其他地方的大量其他圖像處理類提供了基礎 javax.imageio.stream (畢竟,上述類在公共 API 中)。

這意味着,在許多情況下,圖像處理變得更快,并且所有依賴上述任何類(直接或間接)的第三方庫也将運作得更快,而無需更改您的應用程式代碼。

基準

在下面的基準測試中,我使用 Java 17 作為基準,這意味着 Java 21 的其他性能改進也将有助于提高性能。

JDK 21:圖像性能改進

是以,基準方法的吞吐量在我的機器上從大約 579,800,000 位元組/秒提高到大約 639,000,000 位元組/秒,提高了 10% 以上!還不錯!

實際應用程式性能提升

實際上,您的圖像應用程式在 Java 21 下的運作速度有多快?隻有一種方法可以找出答案:今天通過下載下傳 JDK 21 Early-Access Build在 JDK 21 上運作您自己的代碼。

繼續閱讀