Creating Runtime Module for IR to IO , adding libsvm support(wip)#5
Creating Runtime Module for IR to IO , adding libsvm support(wip)#5pantShrey wants to merge 7 commits into
Conversation
| Right(List.empty[Allocation]) | ||
| } | ||
| } | ||
| } yield newAllocs.flatten |
There was a problem hiding this comment.
I wonder if instead of flatten can use flatTraverse.
| case "SVMClassifier" => | ||
| for { | ||
| _ <- checkArity(node, 1, 2) // Ensure SVMClassifier has 2 outputs | ||
| scoresOutputName = node.output(1) |
There was a problem hiding this comment.
Some of the repeated logic is unfortunate here. Not for this PR, but in a follow-up PR I wonder if we can combine both these phases in single-pass. So, the translator would generate both IR operations and manual allocations simultaneously as it traverses the ONNX operations.
| lazy val runtime = project | ||
| .enablePlugins(ScalaNativePlugin, ScalaNativeBrewedConfigPlugin) | ||
| .dependsOn(ir) | ||
| .dependsOn(ir, onnx) |
There was a problem hiding this comment.
Is this just for testing? I think the runtime shouldn't directly depend on ONNX.
| .dependsOn(ir, onnx) | |
| .dependsOn(ir, onnx % Test) |
| /** Copies input Scala Arrays into their corresponding pre-allocated C memory. */ | ||
| private def copyInputsToMemory( |
There was a problem hiding this comment.
Instead of copying the values, we can use Scala Native array.unsafeAt(0) operation to get a Ptr. That is the magic of Scala Native, we can directly do interop without needing to copy data between Scala and native data structures :)
| /** Copies final results from C memory back into new Scala Arrays. */ | ||
| private def copyOutputsFromMemory(model: ModelIR, memory: MemoryMap): IO[Map[String, Array[_]]] = |
There was a problem hiding this comment.
Same thing here, instead of copying the memory back, we can directly write into the result array.
| case other => | ||
| IO.raiseError( | ||
| new NotImplementedError(s"Operation not implemented: ${other.getClass.getSimpleName}"), | ||
| ) |
There was a problem hiding this comment.
Similar idea here, we can actually raise this error outside of the IO so that it surfaces earlier.
| private def handleElementWise(op: Operation, memory: MemoryMap, model: ModelIR)( | ||
| f: (Float, Float) => Float, | ||
| ): IO[Unit] = IO { |
There was a problem hiding this comment.
Unfortunate the f: (Float, Float) => Float will be allocated as a lambda and use dynamic dispatch. The Scala Native Interflow optimizer may be able to specialize this and avoid the lambda allocation and dispatch, but unfortunately it would be better to write these as separate methods with the operation hard-coded, for the guaranteed best performance.
| // This entire block is now a single IO action that performs the C interop. | ||
| predictionResult <- IO { | ||
| // 1. Fill the svm_node struct with input data from the input tensor. | ||
| for (i <- 0 until numFeatures) { |
There was a problem hiding this comment.
We should use a while loop with manually-incremented counter instead of a for (which desugars to a Range allocation, foreach, and lambda with dynamic dispatch).
| // 2. Prepare for and call the prediction function. | ||
| val nrClass = op.classLabels.size | ||
| val decValuesCount = nrClass * (nrClass - 1) / 2 | ||
| val decisionValuesPtr = stackalloc[CDouble](decValuesCount.toUInt) |
There was a problem hiding this comment.
I see that you are returning decisionValuesPtr at the end of the IO, but this is not safe to do with stackallocated memory. The stack is popped at the end of the IO, so that memory is no longer safe to access.
| // Using a for-comprehension here breaks the logic into clean, sequential IO steps. | ||
| for { |
There was a problem hiding this comment.
Actually, I think it would be okay to write the logic within a single IO.
| * @return | ||
| * An IO containing a map of output tensor names to their resulting Scala arrays. | ||
| */ | ||
| def execute(model: ModelIR, inputs: Map[String, Array[_]]): IO[Map[String, Array[_]]] = { |
There was a problem hiding this comment.
| def execute(model: ModelIR, inputs: Map[String, Array[_]]): IO[Map[String, Array[_]]] = { | |
| def execute(model: ModelIR, inputs: Map[String, Array[_]]): Resource[IO, IO[Map[String, Array[_]]]] = { |
| memoryResource(model, inputs, outputArrays) | ||
| .use { memoryMap => |
There was a problem hiding this comment.
| memoryResource(model, inputs, outputArrays) | |
| .use { memoryMap => | |
| memoryResource(model, inputs, outputArrays) | |
| .flatMap { memoryMap => |
| op: Operation.SVMClassifier, | ||
| memory: MemoryMap, | ||
| model: ModelIR, | ||
| ): IO[Unit] = { |
There was a problem hiding this comment.
| ): IO[Unit] = { | |
| ): Resource[IO, IO[Unit]] = { |
| svmInputNode <- malloc[svm_node](sizeof[svm_node] * (numFeatures + 1).toUSize) | ||
| } yield (modelPtr, svmInputNode) | ||
|
|
||
| svmResources.use { case (modelPtr, svmInputNode) => |
There was a problem hiding this comment.
| svmResources.use { case (modelPtr, svmInputNode) => | |
| svmResources.map { case (modelPtr, svmInputNode) => |
…ately executing it added test suite for interpreter
The outputs are inconsistent
Sometimes i get proper response like
But majorly I am getting error