背景
将原始值(例如 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));
}
}
- 通過位移位進行大端解包
- 通過位移位進行小端解包
這裡使用的方案與我在上一篇文章中描述的方案類似 ,是以我不會再深入細節。簡而言之,這種方法複雜且具有挑戰性,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 的其他性能改進也将有助于提高性能。
是以,基準方法的吞吐量在我的機器上從大約 579,800,000 位元組/秒提高到大約 639,000,000 位元組/秒,提高了 10% 以上!還不錯!
實際應用程式性能提升
實際上,您的圖像應用程式在 Java 21 下的運作速度有多快?隻有一種方法可以找出答案:今天通過下載下傳 JDK 21 Early-Access Build在 JDK 21 上運作您自己的代碼。