【问题标题】:Best method to measure execution time in Android?在Android中测量执行时间的最佳方法?
【发布时间】:2021-12-12 03:26:45
【问题描述】:

测量 Android 代码 sn-p 执行时间的最佳方法是什么?

我有一段代码之前和之后我想放置时间戳以找出它的执行时间(例如,一个在onCreate() 中,另一个在onDestroy() 活动方法中)。

我已经尝试过Time.toMillies(false),但它只返回秒数(最后是常量000)。我还尝试了两个 java 函数:System.currentTimeMillis()System.nanoTime()。 第一个返回毫秒的纪元时间,第二个不返回。

测量执行时间和获得良好精度的最佳方法是什么?

【问题讨论】:

    标签: java android


    【解决方案1】:

    TimingLogger呢?

    来自TimingLogger 文档:

    TimingLogger timings = new TimingLogger(YOUR_TAG, "methodA");
    // ... do some work A ... 
    timings.addSplit("work A");
    // ... do some work B ... 
    timings.addSplit("work B");
    // ... do some work C ... 
    timings.addSplit("work C");
    timings.dumpToLog();
    

    转储将如下所示:

         D/TAG     (3459): methodA: begin
         D/TAG     (3459): methodA:      9 ms, work A
         D/TAG     (3459): methodA:      1 ms, work B
         D/TAG     (3459): methodA:      6 ms, work C
         D/TAG     (3459): methodA: end, 16 ms
    

    不要忘记通过运行来启用您的标签:adb shell setprop log.tag.YOUR_TAG VERBOSE

    【讨论】:

    • 感谢您提供有关setprop 的提示。我不明白为什么dumpToLog() 使用Log.d() 时需要VERBOSE,但显然是这样。
    • YOUR_TAG 在示例中应该是您用于初始化TimingLogger 的任何字符串。示例:new TimingLogger("MyApp", "methodA"); 使用 adb shell setprop log.tag.MyApp VERBOSE
    • @LarsH 之所以需要VERBOSE 是因为当TimingLogger 被初始化时,reset() 方法只有在Log.isLoggable(mTag, Log.VERBOSE) 评估为true 时才启用记录器。
    • 我的诀窍是我克隆 TimingLogger 类并使其不检查 VERBOSE。这可能会有所帮助。
    • fortuneteller TimingLogger 自 API 级别 30 以来一直是 deprecated
    【解决方案2】:

    测量执行时间的最佳方法是什么

    System.nanoTime() 可能是一个不错的选择。例如,Jake Wharton 将其与 Hugo 一起使用。

    并获得良好的精度

    这在严格意义上是不可能的,因为当您的方法正在执行时,设备上可能会发生任何事情。这些外部因素会通过窃取 CPU 时间、占用 I/O 通道等来影响您的时间测量。您需要在多次运行中平均您的测试以尝试平均这些外部因素,并且准确性/精度将受到影响结果。

    而且,正如 Marcin Orlowski 所说,要真正弄清楚为什么您会花费一定的时间,请使用 Traceview。

    【讨论】:

    • 从技术上讲,只有准确性会受到影响。精度始终为纳秒。
    【解决方案3】:

    Kotlin 开发人员

    以毫秒为单位获取经过的时间:

    val elapsedTime= measureTimeMillis {
                // call you method from here or add any other statements
    } 
    

    以纳秒为单位获取时间:

    val elapsedTime= measureNanoTime {
                // call you method from here or add any other statements
    }
    

    【讨论】:

      【解决方案4】:

      我通常使用System.nanoTime() 进行快速测量。

      这样的简单设置

         val startTime =   System.nanoTime()
      
          //DO SOMETHING
      
         Log.e("Measure", TASK took : " +  ((System.nanoTime()-startTime)/1000000)+ "mS\n")
      

      【讨论】:

        【解决方案5】:

        您所询问的内容称为profiling,在 Android 上也有一些工具可以帮助您解决这个问题。请参阅官方开发者网站上的文章Profiling with Traceview and dmtracedump

        【讨论】:

        • 完全同意,尽管在分析模式下应用程序速度较慢,但​​有关线程、方法和花费在它们上的时间百分比的信息是值得的。否则,我会说 System.currentTimeMillis() 是他想要的。
        【解决方案6】:

        为使用 Kotlin 开发 Android 应用程序的人们发布答案;这也是在搜索中作为Android 中 Kotlin 代码性能的第一个主要结果出现的唯一答案。

        这个线程上已经有一个简单的 Kotlin 答案,用于测量毫秒和纳秒的性能 - 但我的解决方案将帮助那些希望在执行完成后同时记录和执行内联函数的人,当涉及不同函数的多个性能测量时,这也是一种更简洁的方法

        这样创建函数:

        //the inline performance measurement method
        private inline fun <T> measurePerformanceInMS(
                logger: (Long) -> Unit,
                function: () -> T)
        : T {
            val startTime = System.currentTimeMillis()
            val result: T = function.invoke()
            val endTime = System.currentTimeMillis()
            logger.invoke( endTime - startTime)
            return result
        }
        
        //the logger function
        fun logPerf(time: Long){
            Log.d("TAG","PERFORMANCE IN MS: $time ms ")
        }
        
        //the function whose performance needs to be checked
        fun longRunningFunction() : Int{
            var x = 0
            for (i in 1..20000) x++
            return x
        }
        

        通过这种方式,您可以在单个函数调用下保持日志记录、性能计算和函数执行,而无需复制代码。

        如果您需要纳秒测量,请使用System.nanoTime()

        用法

        val endResult = measurePerformanceInMS({time -> logPerf(time)}){
                    longRunningFunction()
                }
        

        注意:这里的 'endResult' 将携带被测量的函数的结果。

        【讨论】:

          【解决方案7】:

          除了上述答案之外,还有一个名为 Snippet 的库,可用于测量执行时间。 它还负责记录并将所有相关执行放在日志上。它还负责测量可能跨越多个方法甚至文件的非连续代码跨度。下面是它如何处理连续代码。我们也可以在测量中添加拆分。

          @Override  
          protected void onCreate(@Nullable Bundle savedInstanceState) {  
              // The capture API can be used to measure the code that can be passed as a lambda.  
              // Adding this lambda captures the class, line, thread etc automatically into the logcat.
              // This cannot be use for code that returns a value as the lambda declared for closure is 
              // is a non returning lambda. For the case that could return a value and are a little complex // use the log-token based API demonstrated below.  
              
              // Captures the code as a lambda.  
              Snippet.capture(()-> super.onCreate(savedInstanceState)); 
          }
          

          代码也将在发布版本中变为无操作。

          要配置它,只需尽早在应用程序 onCreate() 中添加以下代码即可。

          if(BuildConfig.DEBUG) {             
                  Snippet.install(new Snippet.MeasuredExecutionPath());      
                  Snippet.newFilter("SampleFilter");      
                  Snippet.addFlag(Snippet.FLAG_METADATA_LINE | Snippet.FLAG_METADATA_THREAD_INFO);      
          } 
          

          https://github.com/microsoft/snippet-timekeeper

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-03-22
            • 1970-01-01
            • 2019-02-05
            • 2016-02-05
            • 2020-08-27
            • 2012-05-14
            • 2011-01-05
            • 1970-01-01
            相关资源
            最近更新 更多