【发布时间】:2021-09-04 17:33:23
【问题描述】:
我最近决定尝试使用 Java 的新孵化矢量 API,看看它的速度有多快。我实现了两种相当简单的方法,一种用于解析 int,另一种用于查找字符串中字符的索引。在这两种情况下,我的矢量化方法与它们的标量等效方法相比都非常慢。
这是我的代码:
public class SIMDParse {
private static IntVector mul = IntVector.fromArray(
IntVector.SPECIES_512,
new int[] {0, 0, 0, 0, 0, 0, 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1},
0
);
private static byte zeroChar = (byte) '0';
private static int width = IntVector.SPECIES_512.length();
private static byte[] filler;
static {
filler = new byte[16];
for (int i = 0; i < 16; i++) {
filler[i] = zeroChar;
}
}
public static int parseInt(String str) {
boolean negative = str.charAt(0) == '-';
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
if (negative) {
bytes[0] = zeroChar;
}
bytes = ensureSize(bytes, width);
ByteVector vec = ByteVector.fromArray(ByteVector.SPECIES_128, bytes, 0);
vec = vec.sub(zeroChar);
IntVector ints = (IntVector) vec.castShape(IntVector.SPECIES_512, 0);
ints = ints.mul(mul);
return ints.reduceLanes(VectorOperators.ADD) * (negative ? -1 : 1);
}
public static byte[] ensureSize(byte[] arr, int per) {
int mod = arr.length % per;
if (mod == 0) {
return arr;
}
int length = arr.length - (mod);
length += per;
byte[] newArr = new byte[length];
System.arraycopy(arr, 0, newArr, per - mod, arr.length);
System.arraycopy(filler, 0, newArr, 0, per - mod);
return newArr;
}
public static byte[] ensureSize2(byte[] arr, int per) {
int mod = arr.length % per;
if (mod == 0) {
return arr;
}
int length = arr.length - (mod);
length += per;
byte[] newArr = new byte[length];
System.arraycopy(arr, 0, newArr, 0, arr.length);
return newArr;
}
public static int indexOf(String s, char c) {
byte[] b = s.getBytes(StandardCharsets.UTF_8);
int width = ByteVector.SPECIES_MAX.length();
byte bChar = (byte) c;
b = ensureSize2(b, width);
for (int i = 0; i < b.length; i += width) {
ByteVector vec = ByteVector.fromArray(ByteVector.SPECIES_MAX, b, i);
int pos = vec.compare(VectorOperators.EQ, bChar).firstTrue();
if (pos != width) {
return pos + i;
}
}
return -1;
}
}
我完全预计我的 int 解析会更慢,因为它处理的向量大小永远不会超过可以容纳的范围(int 长度永远不能超过 10 位)。
根据我的基准,将 123 解析为 int 10k 次对于 Integer.parseInt 需要 3081 微秒,而对于我的实现则需要 80601 微秒。在很长的字符串 ("____".repeat(4000) + "a" + "----".repeat(193)) 中搜索 'a' 需要 7709 微秒到 String#indexOf 的 7。
为什么速度如此之慢?我认为 SIMD 的全部意义在于它比此类任务的标量等价物更快。
【问题讨论】:
-
您在什么硬件(和 JVM 版本)上进行了测试?此外,您应该在我的回答中使用您的 cmets 中的信息更新此内容,显然长字符串测试只是重复一次。
标签: java vectorization simd