7

我创建了一个类来处理我的调试输出,这样我就不需要在发布之前删除所有的日志输出。

public class Debug {
    public static void debug( String module, String message) {
        if( Release.DEBUG )
            Log.d(module, message);
    }
}

在阅读了另一个问题后,我了解到如果常量 Release.DEBUG 为 false,则不会编译 if 语句的内容。

我想知道的是运行这个空方法会产生多少开销?(一旦删除了 if 子句,方法中就没有代码了)它会对我的应用程序产生任何影响吗?显然,在为手机编写时,性能是一个大问题 =P

谢谢

加里

4

5 回答 5

14

Measurements done on Nexus S with Android 2.3.2:

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

control:

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

I've repeated the measurements several times. No significant deviations were found. You can see that the per-call cost can vary greatly depending on workload (possibly due to JIT compiling), but 3 conclusions can be drawn:

  1. dalvik/java sucks at optimizing dead code

  2. static function calls can be optimized much better than non-static (non-static functions are virtual and need to be looked up in a virtual table)

  3. the cost on nexus s is not greater than 70ns/call (thats ~70 cpu cycles) and is comparable with the cost of one empty for loop iteration (i.e. one increment and one condition check on a local variable)

Observe that in your case the string argument will always be evaluated. If you do string concatenation, this will involve creating intermediate strings. This will be very costly and involve a lot of gc. For example executing a function:

void empty(String string){
}

called with arguments such as

empty("Hello " + 42 + " this is a string " + count );

10^4 iterations of 100 such calls takes 10s. That is 10us/call, i.e. ~1000 times slower than just an empty call. It also produces huge amount of GC activity. The only way to avoid this is to manually inline the function, i.e. use the >>if<< statement instead of the debug function call. It's ugly but the only way to make it work.

于 2011-02-15T21:55:37.427 回答
2

In terms of performance the overhead of generating the messages which get passed into the debug function are going to be a lot more serious since its likely they do memory allocations eg

Debug.debug(mymodule, "My error message" + myerrorcode);

Which will still occur even through the message is binned. Unfortunately you really need the "if( Release.DEBUG ) " around the calls to this function rather than inside the function itself if your goal is performance, and you will see this in a lot of android code.

于 2010-12-03T17:35:24.720 回答
2

一个好的编译器会删除整个空方法,根本不会产生任何开销。我不确定 Dalvik 编译器是否已经这样做了,但我怀疑这很可能,至少自从带有 Froyo 的即时编译器出现以来。

另请参阅:内联扩展

于 2010-12-03T13:21:07.043 回答
2

除非您从深度嵌套的循环中调用它,否则我不会担心它。

于 2010-12-03T13:16:25.140 回答
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。话虽如此,运行空方法似乎仍有一些非零成本。

于 2016-04-06T01:12:22.840 回答