当前位置: 首页 > 面试题库 >

DecimalFormat.format()的更快替代方法?

羿昊英
2023-03-14
问题内容

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

我正在DecimalFormat.format()与该0.000000模式一起使用,以将double数字“转换”
为正好有六个十进制数字的字符串表示形式。我知道这种方法相对昂贵并且 多次调用,但是我对这些结果感到有些惊讶。

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

  2. 有比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() 每次通话2.7us时速度降低了两倍。
  • 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)的正/负值。如果这可能是一个问题,则可以添加特殊处理。



 类似资料:
  • 问题内容: 我知道这个话题已经解决了上千次。但是我找不到解决办法。 我正在尝试计算列表(df2.list2)的列中出现列表(df1.list1的每一行)的频率。所有列表仅包含唯一值。List1包含约300.000行,list2包含30.000行。 我有一个有效的代码,但是它的运行速度非常慢(因为我使用的是迭代程序)。我也尝试过itertuples(),但它给了我一个错误(“要解压缩的值太多(预期2

  • 问题内容: replace方法返回一个字符串对象而不是替换给定字符串的内容这一事实有点让人费解(但是,当您知道字符串在Java中是不可变的时,这是可以理解的)。通过在某些代码中使用深度嵌套的替换,我的性能受到了重大影响。有什么我可以替换的东西可以使它更快吗? 问题答案: 这就是StringBuilder的目的。如果要进行很多操作,请在上进行操作,然后在需要时将其转换为。 因此描述: “可变的字符序

  • 问题内容: 我有一个3d数组,其中填充了从0到N的整数。我需要一个与该数组等于1、2、3,… N的位置对应的索引列表。我可以使用np.where进行如下操作: 但这很慢。根据这个问题 快速python numpy在哪里功能? 应该可以大大加快索引搜索的速度,但是我无法将那里提出的方法转移到我获取实际索引的问题上。加快上述代码的最佳方法是什么? 作为附加组件:我想稍后存储索引,使用np.ravel_

  • Python的http.server(或Python 2的SimpleHTTPServer)是从命令行提供当前目录内容的一种很好的方式:

  • null 在当前的JVMTI代理中已经被大量使用,所以我正在寻找一种更快的方法来获取标记或实现我自己的机制,同时保持在C端。

  • 问题内容: 我正在制作一个程序,要求至少每秒捕获24个屏幕截图。目前,使用下面的代码,我每94毫秒仅获得1个,因此大约为10毫秒。 我不想使用任何第三方库,因为我试图将其保持尽可能小,但是如果我希望获得显着的性能提升,我会愿意的。我也试图保持该平台独立,但是,如果确实能够显着提高性能,我愿意将其限于Windows。 编辑:我现在也尝试了两种不同的方法;使用在oracles网站上找到的代码段,并在下