Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed functions like <| up without forcing it to be builtin #6394

Closed
4 tasks
JaroslavTulach opened this issue Apr 24, 2023 · 3 comments
Closed
4 tasks

Speed functions like <| up without forcing it to be builtin #6394

JaroslavTulach opened this issue Apr 24, 2023 · 3 comments

Comments

@JaroslavTulach
Copy link
Member

JaroslavTulach commented Apr 24, 2023

#6269 asked for speeding up <| function. That has happened, benchmarks were written. However only because of Function.<| is a builtin. The goal of this issue is to turn the <| function into regular function and still speed it up.

Checklist

  • Improve the speed of any functions that has no local variables by implementing InlineableRootNode
  • Change Function.<| to not be a builtin
  • Verify unit tests continue to work
  • Verify benchmarks remain the same
@wdanilo
Copy link
Member

wdanilo commented Apr 24, 2023

@JaroslavTulach I understand that this is not something we will handle right now, right? At least this is what I understood from our conversation today.

@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Apr 24, 2023

Once #6384 is out, this is a prototpye that can turn Function.<| into real function:

diff --git distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso
index de090ec357..3c0d337f65 100644
--- distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso
+++ distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso
@@ -25,7 +25,7 @@ type Function
                 y = 1 ^ 3
                 3 + y
     <| : Any -> Any
-    <| self ~argument = @Builtin_Method "Function.<|"
+    <| self ~argument = self argument
 
     ## Composes two functions together, for `f << g` creating the function
        composition `f ∘ g` (equivalent to `x -> f (g x)`).
diff --git engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java
index 55c71f9905..261e1d6f10 100644
--- engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java
+++ engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java
@@ -1,8 +1,10 @@
 package org.enso.interpreter.node;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.ReportPolymorphism;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
@@ -14,7 +16,7 @@ import org.enso.interpreter.runtime.scope.ModuleScope;
 
 @ReportPolymorphism
 @NodeInfo(shortName = "Method", description = "A root node for Enso methods.")
-public class MethodRootNode extends ClosureRootNode {
+public class MethodRootNode extends ClosureRootNode implements InlineableRootNode {
 
   private final Type type;
   private final String methodName;
@@ -121,6 +123,32 @@ public class MethodRootNode extends ClosureRootNode {
     return super.copy();
   }
 
+  @Override
+  public DirectCallNode createDirectCallNode() {
+    if (getFrameDescriptor().getNumberOfSlots() == 3) {
+      if (isSimpleMethod()) {
+        if (getBody() instanceof LazyBodyNode lazy) {
+          var c = new MethodRootNode(
+            this.getLanguage(EnsoLanguage.class), 
+            this.getLocalScope(), 
+            this.getModuleScope(), 
+            new LazyBodyNode(lazy.provider), 
+            this.getSourceSection(), 
+            this.getType(), 
+            this.getMethodName()
+          );
+          System.err.println("direct all to clone: " + c);
+          return DirectCallNode.create(c.getCallTarget());
+        }
+      }
+    }
+    return null;
+  }
+
+  private boolean isSimpleMethod() {
+    return type.getName().equals("Function") && methodName.equals("<|");
+  }
+
   private static class LazyBodyNode extends ExpressionNode {
     private final Supplier<ExpressionNode> provider;
 
@@ -130,6 +158,9 @@ public class MethodRootNode extends ClosureRootNode {
 
     static void replaceLazyNode(Node n) {
       if (n instanceof LazyBodyNode lazy) {
+        if (n.getRootNode() instanceof MethodRootNode r && r.isSimpleMethod()) {
+          Thread.dumpStack();
+        }
         lazy.replaceItself();
       }
     }
diff --git engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java
index ad5d399673..034a534b3e 100644
--- engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java
+++ engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java
@@ -11,7 +11,7 @@ import org.enso.interpreter.runtime.state.State;
 
 @BuiltinMethod(
     type = "Function",
-    name = "<|",
+    name = "nic",
     description = "Takes a function and an argument and applies the function to the argument.")
 public class ApplicationOperator extends Node {
   private @Child InvokeCallableNode invokeCallableNode;
diff --git engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
index 9980a306c2..4e414dfc5e 100644
--- engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
+++ engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
@@ -1637,10 +1637,9 @@ class IrToTruffle(
     ) {
       private val argFactory = new DefinitionArgumentProcessor(scopeName, scope)
       private lazy val slots = computeSlots()
-      private lazy val bodyN = computeBodyNode()
 
       def args(): Array[ArgumentDefinition] = slots._2
-      def bodyNode(): RuntimeExpression     = bodyN
+      def bodyNode(): RuntimeExpression     = computeBodyNode()
 
       private def computeBodyNode(): RuntimeExpression = {
         val (argSlotIdxs, _, argExpressions) = slots

Of course the isSimpleMethod implementation and other "solutions" are more a shortcut than a solution. But they seem to show a possible way to move forward.

@JaroslavTulach JaroslavTulach changed the title Speed functions like <| up. Vol II. Speed functions like <| up without forcing it to be builtin Apr 25, 2023
@jdunkerley jdunkerley moved this from ❓New to 📤 Backlog in Issues Board Apr 25, 2023
@jdunkerley jdunkerley removed this from the Beta Release milestone Jul 11, 2023
@jdunkerley
Copy link
Member

Closing for now.

@jdunkerley jdunkerley closed this as not planned Won't fix, can't repro, duplicate, stale Jan 9, 2024
@github-project-automation github-project-automation bot moved this from 📤 Backlog to 🟢 Accepted in Issues Board Jan 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

3 participants