代码之家  ›  专栏  ›  技术社区  ›  Raman Mishra

while循环和递归占用的时间

  •  1
  • Raman Mishra  · 技术社区  · 6 年前

    例如:如果我在一开始为循环编写代码,那么它比递归要花更多的时间,反之亦然。在这两个过程中所花费的时间之间的差异是显著的大约30到40倍。

    我的问题are:-

    1. 循环和递归的顺序重要吗?
    2. 有什么和印刷有关的吗?

    下面是我在同一个文件中的代码,我使用的语言是scala?

      def count(x: Int): Unit = {
        if (x <= 1000) {
          print(s"$x ")
          count(x + 1)
        }
      }
    
      val t3 = System.currentTimeMillis()
      count(1)
      val t4 = System.currentTimeMillis()
      println(s"\ntime taken by the recursion look = ${t4 - t3} mili second")
    
      var c = 1
    
      val t1 = System.currentTimeMillis()
      while(c <= 1000)
        {
          print(s"$c ")
          c+=1
        }
      val t2 = System.currentTimeMillis()
    
      println(s"\ntime taken by the while loop = ${t2 - t1} mili second")
    

    在这种情况下,递归和while循环所用的时间分别为986ms和20ms。

    当我切换循环和递归的位置,即先循环再递归时,递归和while循环所用的时间分别是1.69秒和28毫秒。

    编辑1: 如果递归代码在上面,我可以看到bufferWriter的相同行为。但当递归在循环之下时就不是这样了。当递归低于循环时,所用的时间几乎相同,相差2到3毫秒。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Andrey Tyukin    6 年前

    • 抛开前几次迭代,让JIT有时间醒来并进行热点优化
    • 多轮测量
    • 随机化每轮中变量的顺序,以避免与垃圾收集器的周期发生任何“灾难性共振”
    • 最好不要在计算机上运行任何其他程序

    大致如下:

    def compare(
      xs: Array[(String, () => Unit)],
      maxRepsPerBlock: Int = 10000,
      totalRounds: Int = 100000,
      warmupRounds: Int = 1000
    ): Unit = {
      val n = xs.size
      val times: Array[Long] = Array.ofDim[Long](n)
      val rng = new util.Random
      val indices = (0 until n).toList
      var totalReps: Long = 0
    
      for (round <- 1 to totalRounds) {
        val order = rng.shuffle(indices)
        val reps = rng.nextInt(maxRepsPerBlock / 2) + maxRepsPerBlock / 2
        for (i <- order) {
          var r = 0
          while (r < reps) {
            r += 1
            val start = System.currentTimeMillis
            (xs(i)._2)()
            val end = System.currentTimeMillis
            if (round > warmupRounds) {
              times(i) += (end - start)
            }
          }
        }
        if (round > warmupRounds) {
          totalReps += reps
        }
      }
    
      for (i <- 0 until n) {
        println(f"${xs(i)._1}%20s : ${times(i) / totalReps.toDouble}")
      }
    }
    
    def gaussSumWhile(n: Int): Long = {
      var acc: Long = 0
      var i = 0
      while (i <= n) {
        acc += i
        i += 1
      }
      acc
    }
    
    @annotation.tailrec
    def gaussSumRec(n: Int, acc: Long = 0, i: Int = 0): Long = {
      if (i <= n) gaussSumRec(n, acc + i, i + 1)
      else acc 
    }
    
    compare(Array(
      ("while", { () => gaussSumWhile(1000) }),
      ("@tailrec", { () => gaussSumRec(1000) })
    ))
    

    下面是它打印的内容:

               while : 6.737733046257334E-5
            @tailrec : 6.70325653896487E-5
    

    粗略地

        2
  •  2
  •   Tim    6 年前

    Scala不编译成机器代码,而是编译成“Java虚拟机”(JVM)的字节码,然后JVM在本机处理器上解释代码。JVM使用多种机制来优化频繁运行的代码,最终将频繁调用的函数(“热点”)转换为纯机器代码。

    另外,如评论中所述,执行任何类型的I/O都会使计时变得非常不可靠,因为I/O有阻塞的危险。如果可能的话,写一个不做任何阻塞的测试用例。