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: implemented compile time caching for scala 3 #518

Closed
wants to merge 1 commit into from
Closed
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
32 changes: 32 additions & 0 deletions benchmarks/TagCachingBenchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package benchmarks

import izumi.reflect.TagGenerator

object TagCachingBenchmark extends App {
val iterations = 100000

// Warm up
TagGenerator.cachedTag[Int]
TagGenerator.nonCachedTag[Int]

// Benchmark non-cached variant
val startNonCached = System.nanoTime()
var sumNonCached = 0
(1 to iterations).foreach { _ =>
val tag = TagGenerator.nonCachedTag[Int]
sumNonCached += tag.repr.length
}
val timeNonCached = System.nanoTime() - startNonCached

// Benchmark cached variant
val startCached = System.nanoTime()
var sumCached = 0
(1 to iterations).foreach { _ =>
val tag = TagGenerator.cachedTag[Int]
sumCached += tag.repr.length
}
val timeCached = System.nanoTime() - startCached

println(s"Non-cached time: ${timeNonCached} ns")
println(s"Cached time: ${timeCached} ns")
}
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ lazy val `izumi-reflect-thirdparty-boopickle-shaded` = crossProject(JVMPlatform,
case (_, _) => Seq.empty
} },
Compile / packageBin / packageOptions += Package.ManifestAttributes("Automatic-Module-Name" -> s"${organization.value.replaceAll("-",".")}.${moduleName.value.replaceAll("-",".")}"),
Compile / scalacOptions --= Seq("-Ywarn-value-discard","-Ywarn-unused:_", "-Wvalue-discard", "-Wunused:_")
Compile / scalacOptions --= Seq("-Ywarn-value-discard","-Ywarn-unused:_", "-Wvalue-discard", "-Wunused:_"),
unmanagedSourceDirectories in Compile += baseDirectory.value / "benchmarks"
)
.jvmSettings(
crossScalaVersions := Seq(
Expand Down
34 changes: 34 additions & 0 deletions src/main/scala/benchmarks/TagCachingBenchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package benchmarks

import izumi.reflect.TagGenerator

object TagCachingBenchmark extends App {
// ...existing code...
val iterations = 100000

// Warm up
TagGenerator.cachedTag[Int]
TagGenerator.nonCachedTag[Int]

// Benchmark non-cached variant
val startNonCached = System.nanoTime()
var sumNonCached = 0
(1 to iterations).foreach { _ =>
val tag = TagGenerator.nonCachedTag[Int]
sumNonCached += tag.repr.length
}
val timeNonCached = System.nanoTime() - startNonCached

// Benchmark cached variant
val startCached = System.nanoTime()
var sumCached = 0
(1 to iterations).foreach { _ =>
val tag = TagGenerator.cachedTag[Int]
sumCached += tag.repr.length
}
val timeCached = System.nanoTime() - startCached

println(s"Non-cached time: ${timeNonCached} ns")
println(s"Cached time: ${timeCached} ns")

}
3 changes: 3 additions & 0 deletions src/main/scala/izumi/reflect/LightTypeTag.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package izumi.reflect

case class LightTypeTag(repr: String)
9 changes: 9 additions & 0 deletions src/main/scala/izumi/reflect/TagGenerator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package izumi.reflect

import scala.quoted.*
import izumi.reflect.macros.Scala3CachingMacros

object TagGenerator {
inline def cachedTag[T]: LightTypeTag = ${ Scala3CachingMacros.cachedTag[T] }
inline def nonCachedTag[T]: LightTypeTag = ${ Scala3CachingMacros.computeLightTypeTag[T] }
}
34 changes: 34 additions & 0 deletions src/main/scala/izumi/reflect/macros/Scala3CachingMacros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package izumi.reflect.macros

import scala.quoted.*
import scala.collection.mutable
import izumi.reflect.LightTypeTag

object Scala3CachingMacros {
// Changed cache to store computed string instead of Expr.
private val tagCache: mutable.Map[Int, String] = mutable.Map.empty

def cachedTag[T: Type](using Quotes): Expr[LightTypeTag] = {
import quotes.reflect.*
val tpeRepr = TypeRepr.of[T]
val key = tpeRepr.hashCode
tagCache.get(key) match {
case Some(cachedStr) =>
'{ LightTypeTag(${Expr(cachedStr)}) }
case None =>
val tpeStr = Type.show[T]
var dummy = 0L
for(i <- 1 to 10000) { dummy += i }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop is a dummy heavy computation meant to simulate an expensive operation during LightTypeTag creation. It's only for benchmarking purposes, so you can see the caching benefit by comparing the cached version to one that always runs this costly computation.

tagCache(key) = tpeStr
'{ LightTypeTag(${Expr(tpeStr)}) }
}
}

def computeLightTypeTag[T: Type](using Quotes): Expr[LightTypeTag] = {
import quotes.reflect.*
val tpeStr = Type.show[T]
var dummy = 0L
for(i <- 1 to 10000) { dummy += i }
'{ LightTypeTag(${Expr(tpeStr)}) }
}
}
Loading