我有一个时间敏感的应用程序从第三方库中获取数据。将它们的对象包装到适用于我的应用程序的更合适的接口中会带来怎样的性能下降?包装对象的实际开销是多少?
注:我在这里张贴的答案(Q &样式),但如果它是错的请指正!
我有一个时间敏感的应用程序从第三方库中获取数据。将它们的对象包装到适用于我的应用程序的更合适的接口中会带来怎样的性能下降?包装对象的实际开销是多少?
注:我在这里张贴的答案(Q &样式),但如果它是错的请指正!
还有的一些间接的开销,但很难衡量。 OP的基准测试每次迭代大约需要4 ns,而我的需求大约需要1 ns(用于最快的实验)。这意味着他们主要测量ArrayList
,Iterator
和cycle
的开销,可能还会伴随虚拟通话开销。
要测量的开销非常小,您需要使用数组并添加一个内部循环或使用掩码来访问它们。
我的benchmark的results表明,使用接口和间接方法都有可测量的开销。这个开销范围可能从20%到50%,看起来很多。然而,重要的部分是什么的20-50%。这是特制基准的一小部分,除了行使代码之外别无他法。在任何现实的代码中,相对的开销将会降低十倍,一百倍或千倍。
所以,除非你正在设计一个高性能的库来做一些非常基本和快速的操作,否则就把它忘掉吧。随意使用间接和接口,并专注于良好的设计。即使表现很重要,也可能有其他地方可以获得更多收益。
我认为在这种情况下,开销的重要性不如百分比,更重要。您的最后一段解释了原因。谢谢你做这个工作来同意我的回答。虽然性能在我的应用程序中非常重要,但我很高兴看到我可以通过不包装数据馈送而不必担心显着的性能损失来阻止我的头靠在墙上。 – durron597
在试图与他们的班级工作数月后,今天我决定做一个测试。看起来,如果有任何开销,它不会增加太多。下面是结果 - 这实际上并不那么一致的,这里有一个,实际上已经解开是慢:
0% Scenario{vm=java, trial=0, benchmark=Unwrapped} 3.96 ns; ?=0.02 ns @ 3 trials
33% Scenario{vm=java, trial=0, benchmark=Copy} 3.93 ns; ?=0.01 ns @ 3 trials
67% Scenario{vm=java, trial=0, benchmark=Backing} 3.94 ns; ?=0.01 ns @ 3 trials
benchmark ns linear runtime
Unwrapped 3.96 ==============================
Copy 3.93 =============================
Backing 3.94 =============================
vm: java
trial: 0
源代码(卡尺0.5-RC1,番石榴2.0+):
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
import com.google.common.collect.Iterables;
public class WrapperTest {
public static class Unwrapped {
private int inner;
public Unwrapped(int inner) {
this.inner = inner;
}
public int getInner() {
return inner;
}
}
public static interface Outer {
public int getOuter();
}
public static class CopyOuter implements Outer {
private int outer;
public CopyOuter(int outer) {
this.outer = outer;
}
public int getOuter() {
return outer;
}
}
public static class BackingOuter implements Outer {
private Unwrapped inner;
public BackingOuter(Unwrapped inner) {
this.inner = inner;
}
public int getOuter() {
return inner.getInner();
}
}
public static class TestBenchmark extends SimpleBenchmark {
private Iterable<Unwrapped> cycle;
@Override
protected void setUp() {
List<Unwrapped> backing = new ArrayList<Unwrapped>(16384);
Random r = new Random();
for(int i = 0; i < 16384; i++) {
backing.add(new Unwrapped(Math.abs(r.nextInt())));
}
cycle = Iterables.cycle(backing);
}
public long timeUnwrapped(int reps) {
long total = 0;
Iterator<Unwrapped> iter = cycle.iterator();
for(int i = 0; i < reps; i++) {
total += iter.next().getInner();
}
return total;
}
public long timeCopy(int reps) {
long total = 0;
Iterator<Unwrapped> iter = cycle.iterator();
for(int i = 0; i < reps; i++) {
total += new CopyOuter(iter.next().getInner()).getOuter();
}
return total;
}
public long timeBacking(int reps) {
long total = 0;
Iterator<Unwrapped> iter = cycle.iterator();
for(int i = 0; i < reps; i++) {
total += new BackingOuter(iter.next()).getOuter();
}
return total;
}
}
public static void main(String[] args) {
Runner.main(TestBenchmark.class, new String[0]);
}
}
我真的不认为你的基准是如此精确,所以你可以说3.93真的比3.96快。我猜测量误差总体上是5%左右,在你的情况下可能更多,因为诸如'cycle'之类的东西可能主宰了运行时间。而且,微基准和实际应用的性能差距甚至可能更大。 – maaartinus
我不会在乎包装的成本,除非你包装多次执行的微不足道的方法。我猜JIT可以优化一些成本。 – maaartinus
@maaartinus感谢您的回复。是的,我认为3.93大约等于3.96。创建循环迭代发生在'setUp'中,所以它不会影响运行时。我只是这样做,以确保不会有'IndexOutOfBounds'异常。 – durron597
这将取决于完成的包装类型。 – Raedwald