thk*_*ala 17 java performance profiling string-formatting
为了提高性能,我使用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();
}
Run Code Online (Sandbox Code Playgroud)
因此我预计它的表现非常相似Formatter.format().
Formatter.format():单个Formatter对象重复使用多次.
此方法稍微不方便 - Formatter使用默认构造函数创建的对象将format()方法创建的所有字符串附加到内部StringBuilder对象,该内部对象无法正确访问,因此无法清除.因此,多次调用format()将创建所有结果字符串的串联.
为了解决这个问题,我提供了我自己的StringBuilder实例,我在使用之前清除了一个setLength(0)调用.
有趣的结果:
DecimalFormat.format() 是每次通话1.4us的基线.String.format() 在每次通话2.7便士时慢了两倍.Formatter.format() 在每次通话2.5us时,也慢了两倍.现在它看起来DecimalFormat.format()仍然是这些替代品中最快的.
Pet*_*rey 10
鉴于您确切知道自己想要什么,您可以编写自己的例程.
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);
}
Run Code Online (Sandbox Code Playgroud)
版画
Took 128 ns per append double
Run Code Online (Sandbox Code Playgroud)
如果您想要更高的性能,可以写入直接的ByteBuffer(假设您想在某处写入数据),因此您需要复制或编码您生成的数据.(假设没问题)
注意:这仅限于小于9万亿的正/负值(Long.MAX_VALUE/1e6)如果这可能是个问题,您可以添加特殊处理.