DecimalFormat.format()更快的替代方法?
为了提高性能,我使用VisualVM采样器对我的一个应用程序进行了分析,使用最小采样周期为20ms。 根据分析器,主线程在DecimalFormat.format()
方法中花费了将近四分之一的CPU时间。
我使用DecimalFormat.format()
和0.000000
模式将“ double
数字“转换”为具有正好六位十进制数字的字符串表示形式。 我知道这种方法相对昂贵,并且被称为很多次,但我仍然对这些结果感到有些惊讶。
这样的采样分析器的结果准确到什么程度? 我将如何去验证它们 - 最好不使用仪器分析器?
DecimalFormat
有更快的替代方案吗? 推出我自己的NumberFormat
子类是否有意义?
更新:
我创建了一个微基准来比较以下三种方法的性能:
DecimalFormat.format()
:单次DecimalFormat
对象重复使用多次。
String.format()
:多个独立调用。 这种方法在内部归结为
public static String format(String format, Object ... args) {
return new Formatter().format(format, args).toString();
}
因此我期望它的性能与Formatter.format()
非常相似。
Formatter.format()
:单个Formatter
对象重复使用多次。
这个方法有点尴尬 - 用默认构造函数创建的Formatter
对象将format()
方法创建的所有字符串附加到内部StringBuilder
对象,该对象不能正常访问,因此无法清除。 因此,多次调用format()
将创建所有结果字符串的串联。
为了解决这个问题,我提供了自己的StringBuilder
实例,该实例在使用setLength(0)
调用之前清除。
有趣的结果:
DecimalFormat.format()
是每次调用1.4us的基线。 String.format()
速度减慢了两倍。 Formatter.format()
在每次调用2.5us时也会变慢两倍。 现在看起来DecimalFormat.format()
仍然是这些替代品中最快的。
如果你确切知道你想要什么,你可以编写自己的例程。
public static void appendTo6(StringBuilder builder, double d) {
if (d < 0) {
builder.append('-');
d = -d;
}
if (d * 1e6 + 0.5 > Long.MAX_VALUE) {
// TODO write a fall back.
throw new IllegalArgumentException("number too large");
}
long scaled = (long) (d * 1e6 + 0.5);
long factor = 1000000;
int scale = 7;
long scaled2 = scaled / 10;
while (factor <= scaled2) {
factor *= 10;
scale++;
}
while (scale > 0) {
if (scale == 6)
builder.append('.');
long c = scaled / factor % 10;
factor /= 10;
builder.append((char) ('0' + c));
scale--;
}
}
@Test
public void testCases() {
for (String s : "-0.000001,0.000009,-0.000010,0.100000,1.100000,10.100000".split(",")) {
double d = Double.parseDouble(s);
StringBuilder sb = new StringBuilder();
appendTo6(sb, d);
assertEquals(s, sb.toString());
}
}
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
long start = System.nanoTime();
final int runs = 20000000;
for (int i = 0; i < runs; i++) {
appendTo6(sb, i * 1e-6);
sb.setLength(0);
}
long time = System.nanoTime() - start;
System.out.printf("Took %,d ns per append double%n", time / runs);
}
版画
Took 128 ns per append double
如果你想获得更多的性能,你可以写一个直接的ByteBuffer(假设你想在其他地方写数据),所以你生成的数据不需要被复制或者编码。 (假设没关系)
注意:这限于正负值小于9万亿(Long.MAX_VALUE / 1e6)如果这可能是一个问题,您可以添加特殊处理。
也许你的计划并没有做太多密集的工作,所以这似乎是最有效的 - 处理一些数字。
我的观点是,你的结果仍然是相对于你的应用程序。
在每个DecimalFormatter.format()周围放一个计时器,看看你使用了多少毫秒来获得更清晰的图片。
但是如果你仍然担心这件事,这里有一篇你可能会喜欢的文章:
http://onjava.com/pub/a/onjava/2000/12/15/formatting_doubles.html
另一种方法是使用字符串Formatter,尝试查看它是否更好:
String.format("%.6f", 1.23456789)
或者甚至更好,创建一个格式化程序并重用它 - 只要没有多线程问题,因为格式化程序对于多线程访问不一定是安全的:
Formatter formatter = new Formatter();
// presumably, the formatter would be called multiple times
System.out.println(formatter.format("%.6f", 1.23456789));
formatter.close();
链接地址: http://www.djcxy.com/p/46083.html