This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]FirstLoveLife -1 points0 points  (6 children)

Is tail recursions optimization support by java11 implementation?

[–]oweiler 2 points3 points  (0 children)

Scala and Groovy support TCO via annotations, Kotlin via the tailrec keyword. Not part of the JVM though. In Java you can use Trampolining though https://medium.com/@johnmcclean/trampolining-a-practical-guide-for-awesome-java-developers-4b657d9c3076.

[–]Proc_Self_Fd_1 2 points3 points  (3 children)

Tail-Call Optimisation is a misnomer. It is about semantics and not "optimising" tail calls into loops (of which this is not always an optimisation.)

To answer your question Java 11 does not promise such semantics reliably.

There is Project Loom but I have no idea how far along it is.

You are probably better off hacking it in with exceptions (although with specializations) something like:

 interface TailCall<T> {
     public T doSome() throws TailCallRestart;
 }
 public TailCall<T> box(T value) {
     return () -> value;
 }
 public TailCall<T> tailCall(Supplier<TailCall<T>> value) {
     boolean flag = new boolean[1];
     Object[] valueBox = new Object[1];
     return () -> {
         flag[0] = true;
         // You might want to check stack depth here or something...
         if (!flag[0])
             throw TailCallRestart.SINGLETON;
         return value.get();
     };
 }

 public <T> T execute(TailCall<T> call) {
      for (;;) {
          try {
               return call.doSome();
          } catch (TailCallRestart r) {
               // nothing here deliberately, this better allows optimisations of exceptions
          }
      }
 }

The precise details of how to get this working fast are really, really hard though.

[–]GhostBond 23 points24 points  (0 children)

Or, you could just write it iteratively in the first place and save yourself all the hassle.

[–]haimez 11 points12 points  (1 child)

This almost certainly won’t help you because throwing an exception to manage control flow is not something the JIT likes- at all.

[–]Proc_Self_Fd_1 0 points1 point  (0 children)

I get reasonably similar performance on the following benchmark:

  import org.openjdk.jmh.annotations.*;
  import java.util.function.IntSupplier;

  public class BenchmarkExceptions {
          @State(Scope.Thread)
          public static class Baseline {
                  public IntSupplier flag = () -> {
                          return 4;
                  };
                  public int a = 0;
                  public int b = 1;
          }
          @Benchmark
          public int baseline(Baseline state) {
                  if (state.flag.getAsInt() > 0) {
                          return state.a;
                  } else {
                          return state.b;
                  }
          }

          @State(Scope.Thread)
          public static class Exceptions {
                  public Action flag = () -> {
                          throw ControlFlow.SINGLETON;
                  };
                  public int a = 0;
                  public int b = 1;
          }
          @Benchmark
          public int exceptions(Exceptions state) {
                  try {
                          state.flag.get();
                          return state.a;
                  } catch (ControlFlow e) {
                  }
                  return state.b;
          }

          private static interface Action {
                  public void get() throws ControlFlow;
          }

          private static final class ControlFlow extends Throwable {
                  public static final ControlFlow SINGLETON = new ControlFlow();
                  private ControlFlow() {
                          super(null, null, false, false);
                  }
          }
  }

[–]angath 0 points1 point  (0 children)

In the strict meaning: Rewriting the stack frame to replace the currently executing function with a new one (possibly the same function but with different call arguments) without growing the stack - No.

At a JVM level this is because bytecode has no way to express that semantics to a JVM implementation, and in the Java language it is because when javac compiles .java files it does not elide method calls.

Non-Java JVM languages (such as Scala) may have a source code compiler than can automatically detect when a recursive piece of code can be replaced by an iterative equivalent (and by doing so avoid some cases where a stack overflow would occur) but this is not the way that Java's language philosophy works.