我已经创建了一个类来处理我的调试输出,这样我就不需要在发布之前去掉所有的日志输出。
public class Debug {
public static void debug( String module, String message) {
if( Release.DEBUG )
Log.d(module, message);
}
}
在阅读了另一个问题后,我了解到如果常量Release.DEBUG为false,则不会编译if语句的内容。
我想知道的是运行这个空方法会产生多少开销? (删除if子句后,方法中没有代码)是否会对我的应用程序产生任何影响?显然,在为移动手机编写时,性能是一个大问题= P
由于
加里
答案 0 :(得分:14)
使用Android 2.3.2在Nexus S上进行的测量:
10^6 iterations of 1000 calls to an empty static void function: 21s <==> 21ns/call
10^6 iterations of 1000 calls to an empty non-static void function: 65s <==> 65ns/call
10^6 iterations of 500 calls to an empty static void function: 3.5s <==> 7ns/call
10^6 iterations of 500 calls to an empty non-static void function: 28s <==> 56ns/call
10^6 iterations of 100 calls to an empty static void function: 2.4s <==> 24ns/call
10^6 iterations of 100 calls to an empty non-static void function: 2.9s <==> 29ns/call
控制:
10^6 iterations of an empty loop: 41ms <==> 41ns/iteration
10^7 iterations of an empty loop: 560ms <==> 56ns/iteration
10^9 iterations of an empty loop: 9300ms <==> 9.3ns/iteration
我已多次重复测量。未发现重大偏差。 您可以看到每次呼叫成本可能因工作负载而有很大差异(可能是由于JIT编译), 但可以得出3个结论:
dalvik / java糟透了优化死码
静态函数调用可以比非静态调优得更好 (非静态函数是虚拟的,需要在虚拟表中查找)
nexus s的成本不超过70ns / call(即~70 cpu周期) 并且与一个空循环迭代的成本相当(即一个增量和一个局部变量的一个条件检查)
请注意,在您的情况下,将始终评估字符串参数。如果进行字符串连接,则需要创建中间字符串。这将是非常昂贵的并涉及很多gc。例如执行函数:
void empty(String string){
}
使用诸如
之类的参数调用empty("Hello " + 42 + " this is a string " + count );
100次此类调用的10 ^ 4次迭代需要10次。那是10us / call,即比空呼叫慢〜1000倍。它还会产生大量的GC活动。避免这种情况的唯一方法是手动内联函数,即使用&gt;&gt; if&lt;&lt;语句而不是调试函数调用。这很丑陋,但却是让它发挥作用的唯一方法。
答案 1 :(得分:2)
除非你在一个深层嵌套的循环中调用它,否则我不会担心它。
答案 2 :(得分:2)
一个好的编译器会删除整个空方法,导致根本没有开销。我不确定Dalvik编译器是否已经这样做了,但我怀疑它很可能,至少是因为与Froyo的Just-in-time编译器的到来。
另请参阅:Inline expansion
答案 3 :(得分:2)
就性能而言,生成传递给调试函数的消息的开销将更加严重,因为它可能会进行内存分配,例如
Debug.debug(mymodule, "My error message" + myerrorcode);
即使邮件被分箱,仍然会发生这种情况。 不幸的是,你真的需要围绕调用这个函数的“if(Release.DEBUG)”而不是函数本身,如果你的目标是性能,你会在很多android代码中看到这个。
答案 4 :(得分:1)
这是一个有趣的问题,我喜欢@misiu_mp分析,所以我想我会在运行Android 6.0.1的Nexus 7上进行2016测试更新。这是测试代码:
public void runSpeedTest() {
long startTime;
long[] times = new long[100000];
long[] staticTimes = new long[100000];
for (int i = 0; i < times.length; i++) {
startTime = System.nanoTime();
for (int j = 0; j < 1000; j++) {
emptyMethod();
}
times[i] = (System.nanoTime() - startTime) / 1000;
startTime = System.nanoTime();
for (int j = 0; j < 1000; j++) {
emptyStaticMethod();
}
staticTimes[i] = (System.nanoTime() - startTime) / 1000;
}
int timesSum = 0;
for (int i = 0; i < times.length; i++) { timesSum += times[i]; Log.d("status", "time," + times[i]); sleep(); }
int timesStaticSum = 0;
for (int i = 0; i < times.length; i++) { timesStaticSum += staticTimes[i]; Log.d("status", "statictime," + staticTimes[i]); sleep(); }
sleep();
Log.d("status", "final speed = " + (timesSum / times.length));
Log.d("status", "final static speed = " + (timesStaticSum / times.length));
}
private void sleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void emptyMethod() { }
private static void emptyStaticMethod() { }
添加了sleep()
以防止溢出Log.d
缓冲区。
我玩了很多次,结果与@misiu_mp非常一致:
10^5 iterations of 1000 calls to an empty static void function: 29ns/call
10^5 iterations of 1000 calls to an empty non-static void function: 34ns/call
静态方法调用总是比非静态方法调用略快,但似乎a)差距已经大大关闭,因为Android 2.3.2和b)仍然需要拨打电话费用一个空方法,静态与否。
然而,观察时间直方图显示出一些有趣的东西。大多数呼叫,无论是静态还是非静态,需要30-40ns,仔细观察数据,它们几乎都是30ns。使用空循环运行相同的代码(注释掉方法调用)会产生8ns的平均速度,但是,大约3/4的测量时间是0ns,而其余的正好是30ns。
我不确定如何解释这些数据,但我不确定@ misiu_mp的结论是否仍然有效。空静态和非静态方法之间的差异可以忽略不计,测量的优势正好是30ns。话虽如此,似乎运行空方法仍然有一些非零成本。