Make method actually inline

别等时光非礼了梦想. 提交于 2019-12-05 06:49:03
sjrd

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.

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
        }
      }
    }

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.

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).

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

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.

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

Note that there is a proposal in progress (scala PR 567) to introduce an actual inline keyword. (Sept. 2016)

See "SIP NN: Inline Definitions and Meta Expressions (Public Draft)"

It will work for:

concrete value definitions, e.g.

inline val x = 4

concrete methods, e.g.

inline def square(x: Double) = x * x

parameters of inline methods, e.g.

inline def pow(b: Double, inline n: Int): Double = {
  if (n == 0) 1
  else pow(b, n - 1)
}

Value and method definitions labeled inline are effectively final; they cannot be overridden.
Inline members also never override other members. Instead, every inline member becomes an overloaded alternative of all other members with the same name.

Inline definitions only exist at compile time; no storage is allocated for them in object layout and no code is generated for them in object method tables.
That means that it is OK to have an inline member that has the same type erasure as some other member with the same name.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!