所以我有一个Java程序,我用它来处理几兆字节的数据。表现是一个值得关注的问题。
我分析了这个应用程序,所有内存分配的很大一部分以及CPU时间的很大一部分来自于执行一个简单的操作:
我有一系列的ASCII字符。我知道,从偏移量i到偏移量j的字符代表一个浮点数.我需要把那个浮点数提取到一个double中。
天真的Double.parseDouble(new String(buf, i, j - i))完成了这项工作。然而,这是花费大量时间和大量内存分配的地方,可能是因为:
new String()创建一个新对象,创建一个内部char[]数组并将字符复制到该数组中;Double.parseDouble()创建一个FloatingDecimal对象,也创建一个char[]数组,并将字符复制到其中。所有这些分配和所有这些复制并不是真正必要的。我能避开他们吗?
我真正想要的是一个strtod-like函数,它将接受char[] (或byte[])以及开始/结束偏移,并返回一个double。
有什么建议吗?我应该推出自己的吗?我应该为strtod编写一个JNI包装器吗?我应该使用已经存在的Java库吗?
发布于 2011-09-07 11:06:43
我过去所做的是为ByteBuffer编写一个解析器(以避免字节到字符编码转换)加倍,反之亦然。如果您可以避免创建任何对象,它可能会更快。这种方法适用于内存映射文件,避免了一些复制成本。
核心代码如下所示。它不处理指数,但你可以把它加进去。
@Override
public double read() throws BufferUnderflowException {
long value = 0;
int exp = 0;
boolean negative = false;
int decimalPlaces = Integer.MIN_VALUE;
while (true) {
byte ch = buffer.get();
if (ch >= '0' && ch <= '9') {
while (value >= MAX_VALUE_DIVIDE_10) {
value >>>= 1;
exp++;
}
value = value * 10 + (ch - '0');
decimalPlaces++;
} else if (ch == '-') {
negative = true;
} else if (ch == '.') {
decimalPlaces = 0;
} else {
break;
}
}
return asDouble(value, exp, negative, decimalPlaces);
}完整代码
一旦得到任何它不期望的字节,它就会停止,例如,,或\n
发布于 2011-09-07 10:52:10
我会查看java.lang.Double的源代码,将执行parseDouble的代码复制到我自己的助手类中,并将其修改为直接在char[]上使用offset和length。
发布于 2011-09-07 12:14:48
出于好奇,我将strtod函数复制到Java中,与Double.parseDouble(String)方法相比,获得了10倍的加速比(即使没有在循环中创建新的String)。但也许这对你的实现来说还不够。
微观基准的制定表明:
Double.parseDouble():160万次转换/秒
Java strtod()方法: 10.5M转换/秒
https://stackoverflow.com/questions/7332558
复制相似问题