文档似乎有误。有人能告诉我哪个是真的吗?
在Performance Myths部分中:
在没有JIT的设备上,缓存字段访问比重复访问字段快 20%。使用JIT,字段访问的成本与本地访问的成本大致相同。
在Avoid Internal Getters/Setters部分中:
没有JIT,直接字段访问比调用一个简单的getter快 3x 。使用JIT(直接字段访问与访问本地一样便宜),直接字段访问比调用一个简单的getter快 7x 。
很明显,没有JIT本地访问速度更快。同样清楚的是,访问字段比直接访问更快,而不是使用getter。
但是为什么在第一种情况下性能 20%更好而在第二种情况下性能 133%更好,出于同样的原因,这就是调用对象的JIT优化场?
答案 0 :(得分:5)
我认为你在比较苹果和橘子。 Performance Myths参考讨论了JIT用于字段访问的优势,而第二个参考讨论了JIT用于方法访问的优势。
据我所知,直接字段访问与本地访问(不是您在帖子中写的本地字段访问 - 类似于本地字段之类的东西)的类比如下:
class foo {
int bar = 42;
int doStuff(){
int x = 1;
x += bar;
x += bar;
x += bar;
return x;
}
}
对bar
的每次引用都会产生相关的性能成本。一个好的编译器会认识到优化的机会并“重写”代码:
int doStuff(){
int x = 1f;
int local_bar = bar;
x += local_bar;
x += local_bar;
x += local_bar;
return x;
}
如果没有JIT,这是一个方便的优化,可以让你在性能方面出现 20%。
使用JIT,优化是不必要的,因为JIT首先从访问bar
中删除了性能损失。
第二个参考描述了以下场景:
class foo {
int bar = 42;
int getBar() { return bar; }
int doStuff(){
int x = 1;
x += getBar();
x += getBar();
x += getBar();
return x;
}
}
每个函数调用都有相关的性能损失。编译器可以 NOT 缓存多个getBar()
方法调用(因为它缓存了前一个示例中对bar
的多个直接字段访问),因为getBar()可能会完全返回每次调用时都会有不同的数字(即,如果它的返回值有一个随机或基于时间的组件)。因此,必须执行三个方法调用。
至关重要的是要理解上述函数在有或没有JIT的情况下以相同的速度执行。
如果您仅使用getBar()
手动替换上述功能中的bar
,则可以提升性能。在没有JIT的机器上,性能提升大约是3倍,因为现场访问仍然有点慢,所以用稍慢的字段访问替换非常慢的方法只会产生适度的提升。但是,使用JIT,字段访问速度很快,因此使用快速字段访问替换速度非常慢的方法会产生更大的(7x)增强。
我希望这是有道理的!
答案 1 :(得分:2)
我想你可能会比较苹果和橘子。在第一句话中:
caching field accesses is about 20% faster than repeatedly accesssing the field
意味着缓存策略可以在没有JIT编译的情况下仅在直接字段访问期间提高性能。换句话说:
int a = this.field;
if (a == 1)
...
if (a == 7) // etc.
产生比
更好的性能if (this.field == 1)
....
if (this.field == 7) //etc.
引用建议您通过反复引用该字段而不是将其存储在本地而受到惩罚。
第二个引用表明,如果没有JIT使用琐碎的getter / setter比直接字段访问慢,例如:
if (this.getField()) // etc.
慢于:
if (this.field) // etc.
我认为文档不对,或者一个声明破坏了另一个声明。
答案 2 :(得分:1)
这只是一个有根据的猜测,我不知道Dalvik内部。但请注意,在第一种情况下,将本地访问的性能与字段访问进行比较,而在第二种情况下,将字段访问与简单的方法调用进行比较。另请注意,通过添加JIT,x%加速并不是真正减少x%相同代码的时间,我们讨论的是相对性能:(a)解释的本地访问比解释的字段访问快20%,并且( b)JIT的本地访问速度与JIT的字段访问速度一样快,并不意味着(c)解释的本地访问速度与JIT的本地/字段访问速度一样快。事实上,它很可能更慢。
在解释器中读取本地是大多数VM架构的内存访问,而不是寄存器访问(我们谈论的是机器寄存器,而不是Dalvik寄存器)。读取字段的速度更慢 - 我不能肯定为什么(我的猜测是第二次查找,读取寄存器和对象字段),但无论如何它都更复杂。另一方面,JIT可以将字段和本地都放入寄存器(这是我必须假设的解释性能相等性,事实上有JIT这样做 - 我只是不知道它是否适用于此)和消除了大部分开销。
对于方法调用,假设Dalvik JIT没有内联方法(暗示),你在实际调用之上有相当多的开销,这使得调用成本很高,即使在JIT中:必须将寄存器保存到堆栈,必须恢复它们之后,由于并非所有代码都可见,因此无法进行优化。一个呼叫相对比无呼叫代码更昂贵,因为无呼叫的替代方案非常快,不是因为解释器在做呼叫方面做得更好(它没有,它只是 慢慢做其他事情)。例如,调用不会阻止优化,因为没有优化。