我创建了一个类来处理我的调试输出,因此我不需要在发布前剥离所有日志输出。

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

在阅读了另一个问题之后,我了解到,如果恒定释放是错误的,则没有编译IF语句的内容。

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

谢谢

加里

有帮助吗?

解决方案

用Android 2.3.2对Nexus进行的测量:

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个结论:

  1. Dalvik/Java很糟糕地优化死亡代码

  2. 静态函数调用可以比非静态好得多(非静态功能是虚拟的,需要在虚拟表中查找)

  3. Nexus s的成本不大于70NS/呼叫(那是〜70 CPU周期),并且与一个空的循环迭代的成本相当(即一个增量和一个条件检查本地变量)

观察到,在您的情况下,将始终评估字符串参数。如果进行字符串串联,这将涉及创建中间字符串。这将非常昂贵,并且涉及很多GC。例如执行功能:

void empty(String string){
}

用诸如诸如

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

10^4迭代100个这样的电话需要10秒。那是10US/呼叫,即比一个空通话慢约1000倍。它还产生大量的GC活动。避免这种情况的唯一方法是手动内联函数,即使用>>如果<<语句而不是调试函数调用。这是丑陋的,但唯一使其正常工作的方法。

其他提示

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

一个好的编译器可以删除整个空的方法,从而根本没有开销。我不确定Dalvik编译器是否已经执行此操作,但是我怀疑这很可能是至少自Froyo即时编译器到达以来。

也可以看看: 内联扩展

在性能方面

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

即使通过消息也将发生。不幸的是,您确实需要围绕该功能的调用,而不是在功能本身内部的“ if(repares.debug)”,如果您的目标是性能,则在许多Android代码中看到了这一点。

这是一个有趣的问题,我喜欢@misiu_mp分析,所以我想我会在Nexus 7运行Android 6.0.1的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-40N之间进行,并仔细查看数据,它们实际上完全是30n。

enter image description here

使用空循环运行相同的代码(评论方法调用)的平均速度为8NS,但是,测量时间的3/4是0NS,其余时间正好为30n。

我不确定如何考虑这些数据,但是我不确定 @misiu_mp的结论仍然存在。空静态和非静态方法之间的差异可以忽略不计,测量的优势恰好是30n。话虽这么说,似乎运行空方法仍然存在一些非零的成本。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top