【问题标题】:Fastest way of converting integer to string in java在java中将整数转换为字符串的最快方法
【发布时间】:2013-03-28 05:05:31
【问题描述】:

每次我必须将 int 转换为 String 时,我都会选择 ""+a 或 Integer.toString(a)。现在我想知道哪种方式更快,所以我编写了一个简单的基准测试,调用 function_1、function_2 和 function_3 10000000 次,并打印处理这些函数需要多长时间。以下是函数:

public static String i="";
public static String j="";
public static String k="";

public static void function_1()
{
    i=Integer.toString(getOne());
}

public static void function_2()
{
    j=""+1;
}

public static void function_3()
{
    j=""+getOne();
}

public static int getOne()
{
    return 1;
}

输出是:

Benchmarking starting...
Executing function_1 10000000 time(s)...
Done executing function_1 in 476 ms.
Executing function_2 10000000 time(s)...
Done executing function_2 in 8 ms.
Executing function_3 10000000 time(s)...
Done executing function_3 in 634 ms.
Benchmarking complete!

我觉得function_2这么快,是因为编译成

public static void function_2()
{
    j="1";
}

为了避免这种情况,我改用了函数getOne()。但这里是有趣的部分(对我来说):function_3 必须在不使用 Object 的原始 toString 方法的情况下编译(在这种情况下 Integer.toString(1) 因为int 是原始的)。我的问题是:编译器实际上是如何威胁""+1 所以它比调用Integer.toString(1) 慢?

【问题讨论】:

  • 您考虑过查看字节码吗?此外,微基准通常是无用的。事情也可能发生在 JIT 级别。
  • 如果您查看字节码,您可能会发现function_3 使用StringBuilder 而function_1 没有。在内部,StringBuilder 将调用 String.valueOf()。
  • @D180 - 根据你的数字,function_3 比function_1慢。如果您想知道为什么它“更快”,请不要怀疑:-)
  • @D180 - javap -c MyClass
  • 将整数转换为字符串的最快方法可能是使用 Integer.toString(int)。 (虽然聪明的程序员可能会做得更好。)

标签: java performance optimization benchmarking


【解决方案1】:

"" 和 1 在编译时是已知的。这就是为什么在function_2 "" + 1 在转换为字节码时真正被"1" 替换。

getOne() 结果在编译时未知,因此连接将在运行时完成。但是因为连接 (+) 效率不高,编译器很可能会将其更改为基于 StringBuilder.append() 的实现。

不相信我?试试:javap -c ClassName.class,你会看到这样的:

public static void function_2();
Code:
   0: ldc           #39                 // String 1
   2: putstatic     #16                 // Field j:Ljava/lang/String;
   5: return        


public static void function_3();
Code:
   0: new           #42                 // class java/lang/StringBuilder
   3: dup           
   4: invokespecial #44                 // Method java/lang/StringBuilder."<init>":()V
   7: invokestatic  #28                 // Method getOne:()I
  10: invokevirtual #45                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  13: invokevirtual #49                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  16: putstatic     #16                 // Field j:Ljava/lang/String;
  19: return 

function_2() 只有一个字符串“1”,而 function_3 包含所有这些方法调用,其中包含额外的 StringBuilder :)

请记住,运行时可能会发生一些优化,但这种行为是 JVM 并且取决于配置。

【讨论】:

    【解决方案2】:

    我在 10,000,000 次迭代中测试了以下函数:

    public static void no_func_maybe_constant()
    {
        j= "" + 1;
    }
    
    public static void no_func_no_constant()
    {
        j = "";
        j = j + 1;
    }
    
    public static void yes_func_maybe_constant()
    {
        j = "" + getOne();
    }
    
    public static void yes_func_no_constant()
    {
        j = "";
        j = j + getOne();
    }
    

    我的结果:

    no_func_maybe_constant Took 0.028058674s
    no_func_no_constant Took 1.449465242s
    yes_func_maybe_constant Took 1.275561897s
    yes_func_no_constant Took 1.263362257s
    

    不调用函数和调用函数之间的区别确实可以忽略不计,所以在"" + 1 的情况下,它似乎确实在进行一些编译时常量计算。有趣的是,如果没有函数,有时会花费更少的时间......

    【讨论】:

    • 所以调用函数需要这么多时间?
    • @D180: 是的,调用函数比简单地已有一个常量要花更长的时间。
    • @Claudiu 为什么编译器不内联function_3?
    • @D180 不。我认为您对问题的猜测是正确的。它正在优化为“1”。所以实际上不需要转换。转换可能比函数调用更昂贵。
    • @D180 编译器可以将空函数优化为无操作。一般来说,在 JVM 的动态编译环境中设置微基准非常复杂,并且很难正确猜测编译器实际上在做什么。 ibm.com/developerworks/java/library/j-benchmark1/index.html
    【解决方案3】:

    2 和 3 之间的差异可能是由于必须调用方法来获取整数。当您调用一个方法时,它会创建一个新的激活记录,这会使调用堆栈变得复杂,因此除非 JVM 的 JIT 能够将静态函数调用内联到单个返回值(几乎可以肯定这里不会发生),否则这里会发生更多事情。

    【讨论】:

    • 我希望 JITC 内联该调用。很可能由于某种原因,JITC 没有被激活(这使得整个练习变得虚假,因为解释器的开销淹没了其他一切)。
    猜你喜欢
    • 2010-09-19
    • 2011-04-13
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多