DecimalFormat.format()的更快替代方案? [英] A faster alternative to DecimalFormat.format()?

查看:580
本文介绍了DecimalFormat.format()的更快替代方案?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了提高性能,我使用VisualVM采样器分析了我的一个应用程序,使用的最小采样周期为20ms。根据分析器,主线程花费了近四分之一的CPU时间在 DecimalFormat.format() 方法。

In order to improve its performance, I have been profiling one of my applications with the VisualVM sampler, using the minimum sampling period of 20ms. According to the profiler, the main thread spends almost a quarter of its CPU time in the DecimalFormat.format() method.

我正在使用 DecimalFormat.format()使用 0.000000 模式转换 double 数字到字符串表示正好六位小数。我知道这种方法相对昂贵且调用了很多次,但我仍然对这些结果感到有些惊讶。

I am using DecimalFormat.format() with the 0.000000 pattern to "convert" double numbers to a string representation with exactly six decimal digits. I know that this method is relatively expensive and it is called a lot of times, but I was still somewhat surprised by these results.


  1. 这种采样分析仪的结果在多大程度上是准确的?我将如何验证它们 - 最好不使用仪器分析器?

  1. To what degree are the results of such a sampling profiler accurate? How would I go about verifying them - preferrably without resorting to an instrumenting profiler?

是否有更快的替代 DecimalFormat 我的用例?推出我自己的 NumberFormat 子类是否有意义?

Is there a faster alternative to DecimalFormat for my use case? Would it make sense to roll out my own NumberFormat subclass?

更新:

我创建了一个微基准来比较以下三种方法的表现:

I created a micro-benchmark to compare the performance of the following three methods:


  • DecimalFormat.format():单个 DecimalFormat 对象多次重复使用。

  • DecimalFormat.format(): Single DecimalFormat object reused multiple times.

String.format():多次独立调用。在内部,此方法归结为

String.format(): Multiple independent calls. Internally this method boils down to

public static String format(String format, Object ... args) {
    return new Formatter().format(format, args).toString();
}

因此我预计其表现与非常相似Formatter.format()

Formatter.format():单个 Formatter 对象多次重复使用。

Formatter.format(): Single Formatter object reused multiple times.

此方法略显尴尬 - Formatter 使用默认构造函数创建的对象将 format()方法创建的所有字符串追加到内部 StringBuilder 对象,无法正常访问,因此无法清除。因此,多次调用 format()将创建所有结果字符串的连接

This method is slightly awkward - Formatter objects created with the default constructor append all strings created by the format() method to an internal StringBuilder object, which is not properly accessible and therefore cannot be cleared. As a consequence, multiple calls to format() will create a concatenation of all resulting strings.

要解决这个问题,我提供了自己的 StringBuilder 实例,我在使用它之前清除了 setLength(0)致电。

To work around this issue, I provided my own StringBuilder instance that I cleared before use with a setLength(0) call.

有趣的结果:


  • DecimalFormat.format()是每次通话1.4us的基线。

  • String.format()在每次调用2.7us时减慢了两倍。

  • Formatter.format() 在每次通话2.5us时也减慢了两倍。

  • DecimalFormat.format() was the baseline at 1.4us per call.
  • String.format() was slower by a factor of two at 2.7us per call.
  • Formatter.format() was also slower by a factor of two at 2.5us per call.

现在看起来 DecimalFormat.format()仍然是这些替代品中最快的。

Right now it looks that DecimalFormat.format() is still the fastest among these alternatives.

推荐答案

你可以根据自己的需要编写自己的例程。

You can write your own routine given you know exactly what you want.

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(假设您想在某处写入数据),因此您需要复制或编码您生成的数据。 (假设没问题)

If you want even more performance you can write to a direct ByteBuffer (assuming you want to write the data somewhere) so the data you produce does need to be copied or encoded. (Assuming that is ok)

注意:这仅限于小于9万亿的正/负值(Long.MAX_VALUE / 1e6)如果这样,您可以添加特殊处理可能是个问题。

NOTE: this is limited to positive/negative values of less than 9 trillion (Long.MAX_VALUE/1e6) You can add special handling if this might be an issue.

这篇关于DecimalFormat.format()的更快替代方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆