为什么“新”是关键字比赋值更有效吗? [英] Why is the "new" keyword so much more efficient than assignment?

查看:121
本文介绍了为什么“新”是关键字比赋值更有效吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两种方法可以读取字符串,并创建Character对象:

I've got two methods to read in a string, and create Character objects:

static void newChar(String string) {
    int len = string.length();
    System.out.println("Reading " + len + " characters");
    for (int i = 0; i < len; i++) {
        Character cur = new Character(string.charAt(i));

    }       
}

static void justChar(String string) {
    int len = string.length();
    for (int i = 0; i < len; i++) {
        Character cur = string.charAt(i);

    }
}

当我使用18,554,760个字符串,我的运行时间差异很大。我得到的输出是:

When I run the methods using an 18,554,760 character string, I'm getting wildly different run times. The output I'm getting is:

newChar took: 20 ms
justChar took: 41 ms

输入较小(4,638,690个字符)时,时间不会变化。

With smaller input (4,638,690 characters) the time isn't as varied.

newChar took: 12 ms
justChar took: 13 ms

为什么在这种情况下新的效率更高?

Why is new so much more efficient in this case?

编辑:

我的基准代码非常hacky。

My benchmark code is pretty hacky.

start = System.currentTimeMillis();
newChar(largeString);
end = System.currentTimeMillis();
diff = end-start;
System.out.println("New char took: " + diff + " ms");

start = System.currentTimeMillis();
justChar(largeString);
end = System.currentTimeMillis();
diff = end-start;
System.out.println("just char took: " + diff+ " ms");


推荐答案

TL; DR部分



好消息



您的测量确实会产生实际影响。

TL;DR section

Good news

Your measurement does expose a real effect.

这主要是偶然的,因为你的基准测试有许多技术缺陷,它暴露的效果可能不是你想到的那个。

It does so mostly by chance because your benchmark has many technical flaws, and the effect it exposes is probably not the one you have in mind.

当且仅当 HotSpot的Escape分析成功证明结果时,新的Character()方法更快可以在堆栈而不是堆上安全地分配实例。因此,效果并不像你的问题所暗示的那样一般。

The new Character() approach is faster if and only if HotSpot's Escape Analysis succeeds in proving that the resulting instance can be safely allocated on the stack instead of heap. Therefore the effect is not nearly as general as implied in your question.

之所以如此新的字符()更快引用的位置:您的实例在堆栈上,所有对它的访问都是通过CPU缓存命中。当您重复使用缓存实例时,您必须

The reason why new Character() is faster is locality of reference: your instance is on the stack and all access to it is via CPU cache hits. When you reuse a cached instance, you must


  1. 访问远程 static 字段;

  2. 将其取消引入远程阵列;

  3. 将数组条目取消引用到远程字符实例;

  4. 访问<$ c该实例中包含$ c> char 。

  1. access a remote static field;
  2. dereference it into a remote array;
  3. dereference an array entry into a remote Character instance;
  4. acces the char contained in that instance.

每个取消引用都是潜在的CPU缓存未命中。此外,它强制将部分缓存重定向到这些远程位置,从而导致输入字符串和/或堆栈位置上出现更多缓存未命中。

Each dereference is a potential CPU cache miss. Furthermore, it forces a part of the cache to be redirected towards those remote locations, causing more cache misses on the input string and/or the stack locations.

我用 jmh 运行此代码:

@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.AverageTime)
public class Chars {
  static String string = "12345678901234567890"; static {
    for (int i = 0; i < 10; i++) string += string;
  }

  @GenerateMicroBenchmark
  public void newChar() {
    int len = string.length();
    for (int i = 0; i < len; i++) new Character(string.charAt(i));
  }

  @GenerateMicroBenchmark
  public void justChar() {
    int len = string.length();
    for (int i = 0; i < len; i++) Character.valueOf(string.charAt(i));
  }
}

这保留了代码的本质,但消除了一些系统错误,如预热和编译时间。结果如下:

This keeps the essence of your code, but eliminates some systematic errors like warmup and compilation times. These are the results:

Benchmark              Mode Thr    Cnt  Sec         Mean   Mean error    Units
o.s.Chars.justChar     avgt   1      3    5       39.062        6.587  usec/op
o.s.Chars.newChar      avgt   1      3    5       19.114        0.653  usec/op

这将是我最好的猜测:


  • newChar 您正在创建一个 字符的实例。 HotSpot的Escape分析可以证明实例永远不会逃脱,因此它允许堆栈分配,或者在 Character 的特殊情况下,可以完全消除分配,因为来自它的数据是可证明的从未使用;

  • in newChar you are creating a fresh instance of Character. HotSpot's Escape Analysis can prove the instance never escapes, therefore it allows stack allocation, or, in the special case of Character, could eliminate the allocation altogether because the data from it is provably never used;

justChar 中涉及查询字符缓存数组,一些费用。

in justChar you involve lookup into the Character cache array, which has some cost.

为了回应Aleks的批评,我在基准测试中添加了更多方法。主要效果保持稳定,但我们得到更细微的细节关于较小的优化效果。

In response to Aleks's criticism, I added some more methods to the benchmark. The main effect remains stable, but we get even more fine-grained details about the lesser optimization effects.

  @GenerateMicroBenchmark
  public int newCharUsed() {
    int len = string.length(), sum = 0;
    for (int i = 0; i < len; i++) sum += new Character(string.charAt(i));
    return sum;
  }

  @GenerateMicroBenchmark
  public int justCharUsed() {
    int len = string.length(), sum = 0;
    for (int i = 0; i < len; i++) sum += Character.valueOf(string.charAt(i));
    return sum;
  }

  @GenerateMicroBenchmark
  public void newChar() {
    int len = string.length();
    for (int i = 0; i < len; i++) new Character(string.charAt(i));
  }

  @GenerateMicroBenchmark
  public void justChar() {
    int len = string.length();
    for (int i = 0; i < len; i++) Character.valueOf(string.charAt(i));
  }

  @GenerateMicroBenchmark
  public void newCharValue() {
    int len = string.length();
    for (int i = 0; i < len; i++) new Character(string.charAt(i)).charValue();
  }

  @GenerateMicroBenchmark
  public void justCharValue() {
    int len = string.length();
    for (int i = 0; i < len; i++) Character.valueOf(string.charAt(i)).charValue();
  }



描述:




  • 基本版本为 justChar newChar ;

  • ...值方法将 charValue 调用添加到基本版本;

  • ...使用方法添加 charValue 调用(隐式)并且使用该值来排除任何死代码消除。

  • DESCRIPTION:

    • the base versions are justChar and newChar;
    • ...Value methods add the charValue call to the base version;
    • ...Used methods add both the charValue call (implicitly) and use the value to preclude any Dead Code Elimination.
    • Benchmark                   Mode Thr    Cnt  Sec         Mean   Mean error    Units
      o.s.Chars.justChar          avgt   1      3    1      246.847        5.969  usec/op
      o.s.Chars.justCharUsed      avgt   1      3    1      370.031       26.057  usec/op
      o.s.Chars.justCharValue     avgt   1      3    1      296.342       60.705  usec/op
      o.s.Chars.newChar           avgt   1      3    1      123.302       10.596  usec/op
      o.s.Chars.newCharUsed       avgt   1      3    1      172.721        9.055  usec/op
      o.s.Chars.newCharValue      avgt   1      3    1      123.040        5.095  usec/op
      




      • 某些死代码消除(DCE)的证据都在 justChar NE wChar 变体,但它只是部分;

      • newChar 变体,添加 charValue 没有效果所以显然它是DCE'd;

      • justChar charValue 确实有效果,所以似乎没有消除;

      • DCE的整体效果很小,见证了<之间的稳定差异code> newCharUsed 和 justCharUsed

        • there is evidence of some Dead Code Elimination (DCE) both in justChar and newChar variants, but it is only partial;
        • with newChar variant, adding charValue has no effect so apparently it was DCE'd;
        • with justChar, charValue does have an effect, so seems not to have been eliminated;
        • DCE has a minor overall effect, as witnessed by the stable difference between newCharUsed and justCharUsed.
        • 这篇关于为什么“新”是关键字比赋值更有效吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆