使方法实际内联 [英] Make method actually inline

查看:23
本文介绍了使方法实际内联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我伪造了一个简单的例子来检查 @inline 注释行为:

I forge a simple example to check the @inline annotation behavior:

import scala.annotation.tailrec

object InlineTest extends App {
  @inline
  private def corec(x : Int) : Int = rec(x - 1)

  @tailrec
  private def rec(x : Int) : Int =
    if (x < 3) x else {
      if (x % 3 == 0)
        corec(x-1)
      else
        rec(x-4)
    }

  @tailrec
  private def rec1(x : Int) : Int =
    if (x < 3) x else {
      if (x % 3 == 0) {
        val arg = x - 1
        rec1(arg - 1)
      } else
        rec1(x-4)
    }

  Console.println( rec(args(0).toInt) )
}

这个例子编译时没有警告,tailrec 注释和我想的一样有效.但是当我实际执行代码时,它给了我stackoverflow 异常.这意味着尾递归优化失败,因为内联方法没有那么内联.

This example compiled without warnings, both tailrec annotations were in effect as I thought. But when I actually execute the code, it gives me stackoverflow exception. This means that the tail recursion optimization failed due to inline method being not so inlined.

我有控制函数 rec1,它与原始的不同,只是手动执行了内联"转换.并且由于此函数按预期运行良好,避免了尾递归的 stackoverflow 异常.

I have control function rec1 that differs from the original only with "inline" transformation performed manually. And as this function works well as expected avoiding stackoverflow exception with tail recursion.

是什么阻止了带注释的方法被内联?

What prevents the annotated method from being inlined?

推荐答案

这确实行不通.@inline 的处理比尾调用的处理晚得多,从而阻止了您所希望的优化.

This won't work, indeed. The treatment of @inline is applied way later than the treatment of tail calls, preventing the optimization you're hoping for.

在编译器所谓的尾调用"阶段,编译器尝试转换方法,以便删除尾递归调用并用循环替换.请注意,只有在同一函数内的调用才能通过这种方式消除.因此,在这里,编译器将能够将函数 rec 转换为或多或少等效于以下内容:

During the so-called "tailcalls" phase of the compiler, the compiler tries to transform methods so that tail-recursive calls are removed and replaced by loops. Note that only calls within the same function can be eliminated in this way. So, here, the compiler will be able to transform the function rec into something more or less equivalent to the following:

  @tailrec
  private def rec(x0: Int) : Int =
    var x: Int = x0
    while (true) {
      if (x < 3) x else {
        if (x % 3 == 0)
          return corec(x-1)
        else {
          x = x-4
          continue
        }
      }
    }

请注意,对 corec 的调用并未消除,因为您正在调用另一个方法.那个时候在编译器里面,@inline注解连看都没有.

Note that the call to corec is not eliminated, because you're calling another method. At that time in the compiler, @inline annotations are not even looked at.

但是为什么编译器不警告您它不能消除调用,因为您已经放置了 @tailrec?因为它只检查它实际上是否能够替换对当前方法的所有调用.它不关心程序中的另一个方法是否调用了 rec(即使该方法,在本例中的 corec,本身被 rec 调用).

But why doesn't the compiler warn you that it cannot eliminate the call, since you've put @tailrec? Because it only checks that it is actually able to replace all calls to the current method. It doesn't care whether another method in the program calls rec (even if that method, in this case corec, is itself called by rec).

所以你没有收到警告,但仍然没有消除对 corec 的调用.

So you get no warning, but still the called to corec is not tail-call eliminated.

当您稍后在编译器管道中处理 @inline 时,并假设它确实选择按照您的建议内联 corec,它将再次修改rec 内联 corec 的主体,它给出以下内容:

When you get to the treatment of @inline later in the compiler pipeline, and assuming it does choose to follow your advice to inline corec, it will modify again rec to inline the body of corec, which gives the following:

  @tailrec
  private def rec(x0: Int) : Int =
    var x: Int = x0
    while (true) {
      if (x < 3) x else {
        if (x % 3 == 0)
          return rec((x-1) - 1)
        else {
          x = x-4
          continue
        }
      }
    }

没关系,这会创建一个新的尾递归调用.太晚了.不会再被淘汰了.因此,堆栈溢出.

Never mind then that this creates a new tail-recursive call. It's too late. It won't be eliminated anymore. Hence, the stack overflow.

您可以使用 TailCalls 对象及其方法将相互尾递归方法转换为循环.请参阅API 中的详细信息.

You can use the TailCalls object and its methods to turn mutually tail-recursive methods into loops. See details in the API.

这篇关于使方法实际内联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆