beehive-lab / TornadoVM

TornadoVM: A practical and efficient heterogeneous programming framework for managed languages
https://www.tornadovm.org
Apache License 2.0
1.18k stars 111 forks source link

Support Kotlin #39

Closed yijunwu closed 2 years ago

yijunwu commented 3 years ago

I would like Tornado to support Kotlin. The reason behind that is: . Kotlin is expressive and concise, hence a good fit for data intensive / machine learning tasks, better fit than Java . Kotlin is JVM language, might be relatively easy to support given Java is already supported

jjfumero commented 3 years ago

Hi @yijunwu. We haven't tried to execute from Kotlin yet. Compilation in TornadoVM is from Java bytecode. However, the computation must be expressed in Java because TornadoVM exposes an API for parallel and heterogeneous programming. It would be possible to run Kotlin and call Tornado code from it.

So far, we have tried R, Ruby, Javascript, and NodeJS, using the Truffle Framework (https://github.com/beehive-lab/TornadoVM/tree/master/examples/src/main/java/uk/ac/manchester/tornado/examples/polyglot).

jjfumero commented 2 years ago

I will close this issue. Please, feel free to open new issues for more questions, features, and bug reports.

cromefire commented 7 months ago

Compilation in TornadoVM is from Java bytecode. However, the computation must be expressed in Java because TornadoVM exposes an API for parallel and heterogeneous programming.

Which all should be usable from Kotlin, right? It just compiles to Java Bytecode as well and that same way it can also interface with Java Annotations and APIs, as again it's all just Java Bytecode in the end. I'll definitely give it a try later if I get any example to run and see whether I can get that sample to run as well when ported to Kotlin. I suspect if there are any issues the Kotlin might be generated Bytecode that isn't allowed by TornadoVM or the standard lib, as it seems like not everything is supported.

jjfumero commented 7 months ago

As you point out, I am not sure if the Java BC generated by a Kotlin program are the same as in a Java program. In the case they are the same, I think it is possible to have a compatible version for TornadoVM, but we haven't tried just yet. If you want to try, please keep us posted in your findings. We are really curious.

cromefire commented 7 months ago

I am not sure if the Java BC generated by a Kotlin program are the same as in a Java program

It's largely very similar:

@JvmStatic
fun compute(array: FloatArray) {
    for (i: @Parallel Int in 0 until array.size) {
        array[i] = array[i] + 100
    }
}

results in:

@JvmStatic
public final void compute(@NotNull FloatArray array) {
   Intrinsics.checkNotNullParameter(array, "array");
   int i = 0;
   for(int var3 = array.getSize(); i < var3; ++i) {
      array.set(i, array.get(i) + (float)100);
   }
}

I'm not quite sure what's up with the annotation disappearing, but the java bytecode also look quite "funky" in the java version (apparently the annotation isn't on i, but on some unnamed local variable or something?):

    LOCALVARIABLE i I L1 L2 1
    LOCALVARIABLE array Luk/ac/manchester/tornado/api/types/arrays/FloatArray; L0 L5 0
    LOCALVARIABLE @Luk/ac/manchester/tornado/api/annotations/Parallel;() : LOCAL_VARIABLE, null [ L1 - L2 - 1 ]
    MAXSTACK = 4
    MAXLOCALS = 2

Which in the decompiled version also just disappears:

public static void compute(FloatArray array) {
    for (int i = 0; i < array.getSize(); ++i) {
        array.set(i, array.get(i) + 100.0f);
    }
}

Maybe there's a Kotlin bug to be found here, since this seems to be a rather esoteric usage of annotations...

It doesn't get that far though:

java.lang.NullPointerException: Cannot invoke "jdk.vm.ci.meta.ResolvedJavaType.getAnnotation(java.lang.Class)" because the return value of "org.graalvm.compiler.core.common.type.ObjectStamp.type()" is null
        at tornado.drivers.opencl@1.0.2-dev/uk.ac.manchester.tornado.drivers.opencl.graal.compiler.plugins.OCLVectorPlugins.lambda$registerParameterPlugins$0(OCLVectorPlugins.java:375)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.FrameStateBuilder.initializeForMethodStart(FrameStateBuilder.java:261)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:1026)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:101)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase.run(GraphBuilderPhase.java:63)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase.run(GraphBuilderPhase.java:43)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:434)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:322)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.PhaseSuite.run(PhaseSuite.java:390)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:434)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:322)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.info.elem.InlineableGraph.parseBytecodes(InlineableGraph.java:203)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.info.elem.InlineableGraph.<init>(InlineableGraph.java:76)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.info.elem.Inlineable.getInlineableElement(Inlineable.java:38)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.info.AbstractInlineInfo.populateInlinableElements(AbstractInlineInfo.java:69)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.walker.InliningData.processNextInvoke(InliningData.java:524)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.walker.InliningData.moveForward(InliningData.java:741)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.InliningPhase.run(InliningPhase.java:97)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.common.inlining.InliningPhase.run(InliningPhase.java:40)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:434)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:322)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.PhaseSuite.run(PhaseSuite.java:390)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:434)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:322)
        at tornado.runtime@1.0.2-dev/uk.ac.manchester.tornado.runtime.sketcher.TornadoSketcher.buildSketch(TornadoSketcher.java:162)
        at tornado.runtime@1.0.2-dev/uk.ac.manchester.tornado.runtime.sketcher.TornadoSketcher$TornadoSketcherCallable.call(TornadoSketcher.java:262)
        at tornado.runtime@1.0.2-dev/uk.ac.manchester.tornado.runtime.sketcher.TornadoSketcher$TornadoSketcherCallable.call(TornadoSketcher.java:252)

because it doesn't seem to like that method...

Full sample is here: https://gitlab.com/cromefire/tornado-kotlin

P.S.: ROCm would probably a good idea for AMD cards, OCL just doesn't really seem to work at all as my card just idles at 5% in the benchmarks, it only seemed to spike once for a very short time. Probably not the way to get the most performance out of it. Maybe something is wrong with my setup, but then again, OCL just doesn't seem to be that great... (Even Khronos wants to replace it with Vulkan compute now...)