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

feat: Add writer/reader jitc #207

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
os: [ubuntu-latest]
jvm: [17,19]
benchmark: [BindingsBenchmarks, TransferBenchmarks]
jit: [NoJIT, Standard]
jit: [disabled, standard]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
Expand All @@ -21,7 +21,7 @@ jobs:
with:
jvm: temurin:1.${{ matrix.jvm }}
apps: mill
- run: mill j${{ matrix.jvm }}.benchmarks.test -f1 -wi 2 -i 2 -o j${{ matrix.jvm }}-${{ matrix.os }}.bench -rff j${{ matrix.jvm }}-${{ matrix.os }}.json -rf json .*${{ matrix.benchmark }}${{ matrix.jit }}.*
- run: mill j${{ matrix.jvm }}.benchmarks.test -jvmArgsAppend "-Dslinc.jitc.mode=${{ matrix.jit }}" -f1 -wi 2 -i 2 -o j${{ matrix.jvm }}-${{ matrix.os }}.bench -rff j${{ matrix.jvm }}-${{ matrix.os }}.json -rf json .*${{ matrix.benchmark }}${{ matrix.jvm }}.*
- run: scala-cli run scripts/PublishBenchmarkReport.sc -- "Java ${{ matrix.jvm}}" ${{ matrix.os }} out/j${{ matrix.jvm }}/benchmarks/test/jmhRun.dest/j${{ matrix.jvm }}-${{ matrix.os }}.json ${{ matrix.benchmark }} ${{ matrix.jit }} >> $GITHUB_STEP_SUMMARY
- uses: actions/upload-artifact@v3
with:
Expand Down
8 changes: 2 additions & 6 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ trait BaseModule extends ScoverageModule with ScalafmtModule {
"-unchecked",
"-Xcheck-macros",
"-Xprint-suspension",
"-Xsemanticdb",
"-Yexplicit-nulls",
"-Ysafe-init",
"-source:future",
Expand Down Expand Up @@ -142,12 +141,9 @@ object core
override def scalaVersion = core.scalaVersion()
override def scalacOptions = core.scalacOptions

object test extends BenchmarkSources {
object test extends Benchmarks {
def jmhVersion = jmhV
def forkArgs = super.forkArgs() ++ Seq(
"--add-modules=jdk.incubator.foreign",
"--enable-native-access=ALL-UNNAMED"
)
def forkArgs = super.forkArgs()

}
}
Expand Down
63 changes: 63 additions & 0 deletions core/benchmarks/test/src/fr/hammons/slinc/JitBenchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fr.hammons.slinc

import org.openjdk.jmh.annotations.*
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.infra.Blackhole
import fr.hammons.slinc.jitc.OptimizableFn
import fr.hammons.slinc.jitc.InstantJitFn
import fr.hammons.slinc.jitc.JitCService
import fr.hammons.slinc.jitc.FnToJit
import fr.hammons.slinc.jitc.CountbasedInstrumentation
import fr.hammons.slinc.jitc.IgnoreInstrumentation
import fr.hammons.slinc.jitc.Instrumentation

@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Array(Mode.SampleTime))
class JitBenchmark:
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
def value: Int = 5

val baseFn = (i: Int) => i + 5
val iiFn =
val ignoreInstrumentation: Instrumentation = IgnoreInstrumentation(false)
ignoreInstrumentation((i: Int) => ignoreInstrumentation.instrument(i + 5))
val ciFn =
val countInstrumentation: Instrumentation = CountbasedInstrumentation(10000)
countInstrumentation((i: Int) => countInstrumentation.instrument(i + 5))

val instantFn: OptimizableFn[Int => Int, DummyImplicit] =
InstantJitFn(JitCService.standard, jitc => jitc('{ (i: Int) => i + 5 }))

val bareInstantFn = instantFn.get

val jittedFn: FnToJit[Int => Int, DummyImplicit] = FnToJit(
JitCService.standard,
CountbasedInstrumentation(10000),
jitc => jitc('{ (i: Int) => i + 5 }),
inst => inst((i: Int) => inst.instrument(i + 5))
)

@Benchmark
def base(b: Blackhole) =
b.consume(baseFn(value))

@Benchmark
def ignoreInstrumented(b: Blackhole) =
b.consume(iiFn(value))

@Benchmark
def countInstrumented(b: Blackhole) =
b.consume(ciFn(value))

@Benchmark
def instant(b: Blackhole) =
b.consume(instantFn.get(value))

// @Benchmark
// def bareInstant(b: Blackhole) =
// b.consume(bareInstantFn(value))

@Benchmark
def jitted(b: Blackhole) =
b.consume(jittedFn.get(value))
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fr.hammons.slinc

import org.openjdk.jmh.annotations.*
import java.util.concurrent.atomic.AtomicReference
import fr.hammons.slinc.jitc.JitCService
import fr.hammons.slinc.jitc.JitCompiler
import org.openjdk.jmh.infra.Blackhole
import scala.compiletime.uninitialized
import java.util.UUID
import java.util.concurrent.TimeUnit

@State(Scope.Thread)
class JitCompilerState:
val fnRef: AtomicReference[Int => Int] = AtomicReference()

val jitcAsync = JitCService.standard
var uuid: UUID = uninitialized

val methodToCompile: JitCompiler => Unit = jitc =>
fnRef.setOpaque(jitc('{ (i: Int) => i }))

@Setup(Level.Invocation)
def setup(): Unit =
uuid = UUID.randomUUID().nn
fnRef.set(null)

@State(Scope.Thread)
class DummyState:
val dummyFnToCompile: JitCompiler => Unit = jitc => jitc('{ (i: Int) => i })

val dummyIds: Array[UUID] = Array.ofDim(100)

@Setup(Level.Invocation)
def setup(): Unit =
for i <- 0 until dummyIds.size do dummyIds(i) = UUID.randomUUID().nn

@BenchmarkMode(Array(Mode.SampleTime))
@OutputTimeUnit(TimeUnit.MILLISECONDS)
class JitCompilerBenchmark:

@Benchmark
def compilationSpeed(b: Blackhole, jcs: JitCompilerState) =
b.consume:
jcs.jitcAsync.jitC(jcs.uuid, jcs.methodToCompile)
while jcs.fnRef.get() == null do {}

@Benchmark
@OperationsPerInvocation(101)
def compileStress(b: Blackhole, jcs: JitCompilerState, ds: DummyState) =
b.consume:
for id <- ds.dummyIds do jcs.jitcAsync.jitC(id, ds.dummyFnToCompile)
jcs.jitcAsync.jitC(jcs.uuid, jcs.methodToCompile)
while jcs.fnRef.get() == null do {}
135 changes: 125 additions & 10 deletions core/benchmarks/test/src/fr/hammons/slinc/TransferBenchmarkShape.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package fr.hammons.slinc

import fr.hammons.slinc.types.CLong
import org.openjdk.jmh.annotations.{Scope as _, *}
import org.openjdk.jmh.infra.Blackhole
import fr.hammons.slinc.descriptors.WriterContext
import scala.util.Random
import fr.hammons.slinc.jitc.FnToJit
import fr.hammons.slinc.modules.MemWriter
import scala.compiletime.uninitialized

case class A(a: Int, b: B, c: Int)
case class B(a: Int, b: Int)
case class A(a: Int, b: B, c: Int) derives Struct
case class B(a: Int, b: Int) derives Struct
case class G(a: Int, b: Float, c: CLong) derives Struct
case class I(a: Int, b: Float, c: CLong) derives Struct

//@Warmup(iterations = 5)
//@Measurement(iterations = 5)
trait TransferBenchmarkShape(val s: Slinc):
import s.{given, *}

case class C(a: Int, b: D, c: Int)
case class D(a: Int, b: Int)
given Struct[A] = Struct.derived
given Struct[B] = Struct.derived
given Struct[C] = Struct.derived
given Struct[D] = Struct.derived
given WriterContext = WriterContext(dm, rwm)

case class C(a: Int, b: D, c: Int) derives Struct
case class D(a: CLong, b: Int) derives Struct
case class E(a: Int, b: Int) derives Struct
case class F(a: Int, e: E, c: Int) derives Struct

val aPtr = Scope.global {
Ptr.blank[A]
Expand All @@ -25,7 +36,32 @@ trait TransferBenchmarkShape(val s: Slinc):
Ptr.blank[C]
}

val c = C(1, D(2, 3), 4)
val c = C(1, D(CLong(2), 3), 4)

@CompilerControl(CompilerControl.Mode.DONT_INLINE)
def offset = Bytes(0)

val g = G(1, 2f, CLong(3))

@CompilerControl(CompilerControl.Mode.DONT_INLINE)
def getG = g

val gPtr = Scope.global {
Ptr.blank[G]
}

val i = I(1, 2f, CLong(3))

@CompilerControl(CompilerControl.Mode.DONT_INLINE)
def getI = i
val iPtr = Scope.global:
Ptr.blank[I]

val optimizedIWriter =
summon[DescriptorOf[I]].writer.forceOptimize

@CompilerControl(CompilerControl.Mode.DONT_INLINE)
def getOptimizedIWriter = optimizedIWriter

@Benchmark
def topLevelRead =
Expand All @@ -35,6 +71,55 @@ trait TransferBenchmarkShape(val s: Slinc):
def topLevelWrite =
!aPtr = a

@Benchmark
@Fork(
jvmArgsAppend = Array(
"-Dslinc.jitc.mode=standard"
)
)
def jitted(blackhole: Blackhole) = blackhole.consume:
!gPtr = getG

@Benchmark
@Fork(
jvmArgsAppend = Array(
"-Dslinc.jitc.mode=disabled"
)
)
def compiletime(blackhole: Blackhole) = blackhole.consume:
!gPtr = getG

@Benchmark
@Fork(
jvmArgsAppend = Array(
"-Dslinc.jitc.mode=immediate"
)
)
def immediatecompilation(blackhole: Blackhole) = blackhole.consume:
!gPtr = getG

@Benchmark
def nakedfunction(blackhole: Blackhole) = blackhole.consume:
getOptimizedIWriter(iPtr.mem, iPtr.offset, getI)

import scala.language.unsafeNulls
val castGWriter: FnToJit[MemWriter[G], WriterContext] =
summon[DescriptorOf[G]].writer match
case a: FnToJit[MemWriter[G], WriterContext] => a
case _ => null

var x = Random.nextInt()
var y = Random.nextInt()

@Benchmark
def fntojit(blackhole: Blackhole) = blackhole.consume(
castGWriter.get(gPtr.mem, gPtr.offset, getG)
)

@Benchmark
def addValues(blackhole: Blackhole) = blackhole.consume:
x + y

@Benchmark
def innerRead =
!cPtr
Expand All @@ -50,7 +135,37 @@ trait TransferBenchmarkShape(val s: Slinc):
)

@Benchmark
def allocateIntPointer =
def allocatePrimitivePointer =
Scope.confined(
Ptr.copy(3)
)

@Benchmark
def allocateAliasPointer =
Scope.confined(
Ptr.copy(CLong(3))
)

@Benchmark
def allocateComplexWAliasInnerStructPointer =
Scope.confined(
Ptr.copy(C(1, D(CLong(2), 3), 4))
)

@Benchmark
def allocateSimpleWAliasInnerStructPointer =
Scope.confined(
Ptr.copy(D(CLong(2), 3))
)

@Benchmark
def allocatePtrFromArray =
Scope.confined(
Ptr.copy(Array(1, 2, 3))
)

@Benchmark
def allocatePtrFromCLongArray =
Scope.confined(
Ptr.copy(Array(CLong(1), CLong(2), CLong(3)))
)
22 changes: 11 additions & 11 deletions core/src/fr/hammons/slinc/Alias.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package fr.hammons.slinc

import types.{OS, Arch, os, arch}
import scala.reflect.ClassTag

trait Alias[T] extends DescriptorOf[T]:
lazy val name: String
lazy val aliases: PartialFunction[(OS, Arch), TypeDescriptor]
val descriptor: TypeDescriptor { type Inner >: T <: T } =
AliasDescriptor[T](
aliases.applyOrElse(
os -> arch,
_ =>
throw new Error(
s"Alias for $name is not defined on platform $os - $arch"
)
abstract class Alias[T](
val name: String,
val aliases: PartialFunction[(OS, Arch), TypeDescriptor]
)(using ClassTag[T])
extends DescriptorOf[T](
AliasDescriptor[T](
aliases.applyOrElse(
os -> arch,
_ => throw new Error("")
)
)
)
6 changes: 6 additions & 0 deletions core/src/fr/hammons/slinc/Bytes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package fr.hammons.slinc

import scala.quoted.{ToExpr, Quotes}
import fr.hammons.slinc.types.SizeT
import scala.quoted.FromExpr
import scala.quoted.Expr

opaque type Bytes = Long

Expand Down Expand Up @@ -29,3 +31,7 @@ object Bytes:
given Numeric[Bytes] = Numeric.LongIsIntegral
given ToExpr[Bytes] with
def apply(t: Bytes)(using Quotes) = ToExpr.LongToExpr[Long].apply(t)

given FromExpr[Bytes] with
def unapply(x: Expr[Bytes])(using Quotes) =
FromExpr.LongFromExpr[Long].unapply(x)
Loading