为什么“新”是关键字比赋值更有效吗? [英] Why is the "new" keyword so much more efficient than assignment?
问题描述
我有两种方法可以读取字符串,并创建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
- 访问远程
static
字段; - 将其取消引入远程阵列;
- 将数组条目取消引用到远程
字符
实例; - 访问<$ c该实例中包含$ c> char 。
- access a remote
static
field; - dereference it into a remote array;
- dereference an array entry into a remote
Character
instance; - 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 ofCharacter
. HotSpot's Escape Analysis can prove the instance never escapes, therefore it allows stack allocation, or, in the special case ofCharacter
, 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
调用(隐式)并且使用该值来排除任何死代码消除。 - the base versions are
justChar
andnewChar
; ...Value
methods add thecharValue
call to the base version;...Used
methods add both thecharValue
call (implicitly) and use the value to preclude any Dead Code Elimination.
DESCRIPTION:
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
andnewChar
variants, but it is only partial; - with
newChar
variant, addingcharValue
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
andjustCharUsed
.
这篇关于为什么“新”是关键字比赋值更有效吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!