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的基线。
  • 在每次调用2.7us时, 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

    上一篇: A faster alternative to DecimalFormat.format()?

    下一篇: get value from servlet using ajax