Java 为什么字符串转换循环似乎有静态开销? 背景
我一直在运行一段代码(张贴在底部)来测量显式Java向下广播的性能,我遇到了我觉得有点反常的情况。。。或者可能有两种反常现象 我已经研究了Java转换开销,但它似乎只讨论一般的转换,而不是这个特殊的现象。涵盖了类似的主题,我真的不需要关于过早优化的建议——我正在调整应用程序的某些部分以获得最佳性能,因此这是合乎逻辑的步骤 测试 我基本上是想对Java 为什么字符串转换循环似乎有静态开销? 背景,java,performance,casting,Java,Performance,Casting,我一直在运行一段代码(张贴在底部)来测量显式Java向下广播的性能,我遇到了我觉得有点反常的情况。。。或者可能有两种反常现象 我已经研究了Java转换开销,但它似乎只讨论一般的转换,而不是这个特殊的现象。涵盖了类似的主题,我真的不需要关于过早优化的建议——我正在调整应用程序的某些部分以获得最佳性能,因此这是合乎逻辑的步骤 测试 我基本上是想对Strings,但键入为Objects的对象,测试下行广播与.toString()方法的性能。因此,我创建了一个具有等效内容的字符串a和对象b,运行了三个循
String
s,但键入为Object
s的对象,测试下行广播与.toString()
方法的性能。因此,我创建了一个具有等效内容的字符串a
和对象b
,运行了三个循环,并对它们计时
- 循环1是
((字符串)b)李>
- 循环2是
李>b.toString().toLowerCase()
- 循环3是
a.toLowerCase()
- 循环2是
iters |测试回合|环路1 |环路2 |环路3
-----------|--------------|----------|----------|----------
50,000,000 | 1 | 3367 | 3166 | 3186
测试A | 2 | 3543 | 3158 | 3156
| 3 | 3365 | 3155 | 3169
-----------|--------------|----------|----------|----------
5,000,000 | 1 | 373 | 348 | 369
测试B | 2 | 373 | 348 | 370
| 3 | 399 | 334 | 371
-----------|--------------|----------|----------|----------
500,000 | 1 | 66 | 36 | 33
测试C | 2 | 71 | 36 | 41
| 3 | 66 | 35 | 34
-----------|--------------|----------|----------|----------
50,000 | 1 | 27 | 5 | 5
测试D | 2 | 27 | 6 | 5
| 3 | 26 | 5 | 5
-----------|--------------|----------|----------|----------
用于测试的代码
基准测试是有缺陷的,因为SO和其他地方的大多数问题都与Java代码基准测试有关。您所测量的内容比您想象的要多得多,例如JIT编译方法、热点优化循环等等 检查 此外,服务器虚拟机和客户端虚拟机的行为也不同(JVM在客户端启动更快,但在一段时间内运行较慢,因为它在编译时开始解释字节码,而服务器虚拟机在运行前编译字节码),等等 GC也可能会产生干扰,如果您在基准测试期间获得任何完整GC(通常是每隔一个线程完全暂停一次完整GC),则更是如此。即使是较小的集合也可能有一些影响,因为它们可以使用相当多的CPU来清理循环中可能产生的巨大混乱 要做一个正确的基准测试,您应该“预热”JVM,打开JVM的大量输出,以确定您正在测量什么,等等 在这里检查这个问题,它解决了如何用Java编写基准测试的问题,包括我上面提到的主题以及更详细的内容: 为什么调用.toString()要比已经拥有String对象快 从数字上看,我看不出循环2始终比循环3快。事实上,在某些情况下,速度较慢。测试B中明显的显著差异可能仅仅是GC在循环3的情况下比在循环2的情况下运行一次。这可能只是基准设计的产物
无论如何,如果您真的想知道发生了什么(如果有的话),您需要查看JIT编译器在每种情况下生成的本机指令。(有JVM选项可以这样做…+1用于有趣文章的有趣链接,+1用于有趣的问题!),但我认为如果您对这个基准实例的缺陷给出一些具体的想法,会有所帮助。我浏览了这篇文章,但并不完全清楚它在这里是如何应用的。例如,本文中的整个同步问题是一个错误,在这里不适用。你认为哪些部分适用于这里?好的,你的一些细节是在我写评论的时候讲出来的。这些帮助。很好的答案,很好的链接。当我使用一些编译器输出重新运行基准测试时,它显示在第一个循环中完成了大量编译器的工作。当我在其中添加一个垃圾循环时,结果比我预期的要多。我已经用控制台输出编辑了我的第一篇博文——非常有趣的东西。试着以不同的顺序运行循环和测试。有什么不同吗?例如,如果在测试D中将循环1放在最后会发生什么?Java非常擅长优化,在编译后,循环行为可能完全不同。@DWright极好的调用。在第一个循环中肯定会有一些事情发生。这个输出听起来像
-XX:+printcomployment
和-verbose:gc
,你可以看到很多gc活动(因为字符串是不可变的,循环中的许多调用将生成新字符串,这些字符串将被丢弃——尽管它运行得相当快!)很高兴它工作了。在我离开之前,有一个提示可能会有所帮助:通常在调用方法20k(或10k,取决于客户机/服务器vm)次之后,HotSpot会对这些方法进行优化。因此,为了热身,通常需要在调用方法20k次的循环中复制和粘贴要作为基准的代码
long t, iters = ...;
String a = "String", c;
Object b = "String";
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = ((String) b).toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = b.toString().toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = a.toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
50 1 java.lang.String::toLowerCase (472 bytes)
50 2 java.lang.CharacterData::of (120 bytes)
53 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
53 4 java.lang.Character::toLowerCase (9 bytes)
54 5 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
67 6 n java.lang.System::arraycopy (0 bytes) (static)
68 7 java.lang.Math::min (11 bytes)
68 8 java.util.Arrays::copyOfRange (63 bytes)
69 9 java.lang.String::toLowerCase (8 bytes)
69 10 java.util.Locale::getDefault (13 bytes)
70 1 % Main::main @ 14 (175 bytes)
[GC 49088K->360K(188032K), 0.0007670 secs]
[GC 49448K->360K(188032K), 0.0024814 secs]
[GC 49448K->328K(188032K), 0.0005422 secs]
[GC 49416K->328K(237120K), 0.0007519 secs]
[GC 98504K->352K(237120K), 0.0122388 secs]
[GC 98528K->352K(327552K), 0.0005734 secs]
595 1 % Main::main @ -2 (175 bytes) made not entrant
548 /****** Junk Loop ******/
597 2 % Main::main @ 61 (175 bytes)
[GC 196704K->356K(327552K), 0.0008460 secs]
[GC 196708K->388K(523968K), 0.0005100 secs]
343 /****** Loop 1 ******/
939 2 % Main::main @ -2 (175 bytes) made not entrant
940 11 java.lang.String::toString (2 bytes)
940 3 % Main::main @ 103 (175 bytes)
[GC 393092K->356K(523968K), 0.0036496 secs]
377 /****** Loop 2 ******/
1316 3 % Main::main @ -2 (175 bytes) made not entrant
1317 4 % Main::main @ 145 (175 bytes)
[GC 393060K->332K(759680K), 0.0008326 secs]
320 /****** Loop 3 ******/