Java中使用Vector API实现 Base64编码/解码


该库包含使用孵化器 Vector API 实现的 Base64 编码器和解码器,以及(较慢的)标量实现。这是最快的 Java 编写的 Base64 库,基于向量和标量方法的性能均优于任何其他 Java 编写的库。对于 RFC2045/MIME 编码/解码,这些方法比内在化 java.util 更快,但对于 RFC4648 则较慢(请参阅“java.util 内在化”和“基准”部分)。需要 Java 16 或更高版本。

用于 Vector 实现的编码和解码方法可以在 VectorUtils.java 中找到,其很大程度上受到 Wojciech Mula 文章的启发,可在此处找到:
http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html
http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html

由于 Vector API 当前是一个孵化器项目,因此您需要将标志添加--add-modules jdk.incubator.vector到 java run 命令中才能使用该库的矢量方法。
一旦 Vector API 正式发布,该库将托管在中央 Maven 存储库上。同时,这只能通过本地安装使用。

为了方便起见,可以通过 Base64Vector 和 Base64Scalar 类访问静态实例化的编码器/解码器。您还可以自己实例化单独的编码器/解码器。

java.util Intrinsification
java.util 编码和解码方法依赖于 encodeBlock 和 decodeBlock 方法的使用。这两个方法分别从 Java 11 和 Java 16 开始成为 intrinsinc 候选方法,这意味着 JVM 可以用速度极快的 SIMD 方法替换它们。根据我的测试,在 JMH 基准测试中,这些方法在调用约 5000 次后就会被 intrinsifed。那么,需要解码多少字节才能达到对这些方法的 5000 次调用?

如果根据 RFC4648/RFC4648_URL 规范进行编码或解码,则每次调用编码或解码时都会调用一次本征方法,这显然可以实现最快的速度,因为这样可以最大限度地减少 Java 中的回调。遗憾的是,这意味着高层方法必须被调用 ~5000 次,用户才能从内在化中获益 (*)。如果要处理大型数组,这可能需要一段时间:在我的 M1 Mac 上,10MB 数组的 java.util 编码需要连续编码 33 秒才能实现内在化。同样,如果您的应用程序在其生命周期中编码/解码次数不超过 5000 次,您将永远不会从中受益。如果您的应用程序对 base64 进行的编码/解码是稀疏的,而不是连续的,这也可能会妨碍内在化。

java.util RFC2045 方法的内在化更为一致,并且不受输入大小的影响。编码方法将以 76 字节块为单位对数据进行编码,然后返回 Java 以写入换行符,而解码方法将在每个无效 base64 字符(包括每 76 字节的换行符)时返回 Java。虽然由于 IntrinsicCandidate 方法每 76 字节输入至少被调用一次,这保证了更快的本征化速度,但这大大降低了方法的运行速度,从而使 jdz 向量的执行速度更快。

在每个系统或 JVM 上也不一定都会发生内联化。例如,在 AWS m4.large 或我的 VMWare Ubuntu 虚拟机上,java.util 方法就没有被内在化。因此,在任何此类系统上,jdz 方法的性能都大大优于 java.util,因为 java.util 将执行标量编码/解码。

Github点击标题