Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add some benchmarks for odbv1 memory consumption (#11)
This is more odbv1 code, and a template for future odbv2 benchmarks. Co-authored-by: Michael Pollmeier <michael@michaelpollmeier.com>
- Loading branch information
1 parent
6374f04
commit eefa3fd
Showing
6 changed files
with
147 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/target/ | ||
/project/target/ | ||
/project/project/ | ||
/benchJoern/target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
name := "bench-joern" | ||
enablePlugins(JavaAppPackaging) | ||
|
||
libraryDependencies ++= Seq( | ||
"io.joern" %% "semanticcpg" % "1.1.1455", | ||
"com.jerolba" % "jmnemohistosyne" % "0.2.3", | ||
"org.openjdk.jol" % "jol-core" % "0.16", | ||
"org.slf4j" % "slf4j-simple" % "2.0.6" % Optional, | ||
) |
122 changes: 122 additions & 0 deletions
122
benchJoern/src/main/scala/io/joern/joernBench/bench.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package io.joern.joernBench | ||
import better.files.Dsl.cp | ||
import com.jerolba.jmnemohistosyne.{HistogramEntry, Histogramer, MemoryHistogram} | ||
import io.shiftleft.codepropertygraph.cpgloading.{CpgLoader, CpgLoaderConfig} | ||
import io.shiftleft.codepropertygraph.generated.Cpg | ||
import overflowdb.Config | ||
|
||
import scala.jdk.CollectionConverters.IteratorHasAsScala | ||
import scala.util.{Success, Try} | ||
|
||
object Bench { | ||
|
||
class MeasurementBox { | ||
var timeNanos = -1L | ||
// the @volatile is really just to disincentivize the jvm from clearing up writes to the result | ||
@volatile var result: Any = null | ||
@volatile var histo: MemoryHistogram = null | ||
|
||
def clear(): Unit = { | ||
result = null | ||
} | ||
} | ||
|
||
def measure(code: => Any): MeasurementBox = { | ||
val box = new MeasurementBox | ||
box.histo = Histogramer.getDiff(() => { | ||
val tic = System.nanoTime() | ||
box.result = code | ||
val toc = System.nanoTime() | ||
box.timeNanos = toc - tic | ||
}) | ||
box | ||
} | ||
|
||
def loadCopyFile(filename: String): Cpg = { | ||
val newLoc = better.files.File(filename + ".tmp") | ||
cp(better.files.File(filename), newLoc) | ||
val odbConfig = Config.withDefaults.withStorageLocation(newLoc.toString()) | ||
val config = CpgLoaderConfig.withDefaults.doNotCreateIndexesOnLoad.withOverflowConfig(odbConfig) | ||
CpgLoader.loadFromOverflowDb(config) | ||
} | ||
|
||
def makeIndices(cpg: Cpg): Unit = { | ||
CpgLoader.createIndexes(cpg) | ||
} | ||
|
||
def touchGraph(cpg: Cpg): Int = { | ||
cpg.graph.edgeCount() | ||
} | ||
|
||
def modHisto(histo: MemoryHistogram, top: Int, nodecount: Int): (Long, String) = { | ||
val lst = histo | ||
.iterator() | ||
.asScala | ||
.toBuffer | ||
.sortBy { e: HistogramEntry => -scala.math.abs(e.getSize) } | ||
.take(if (top > 0) top else java.lang.Integer.MAX_VALUE) | ||
.filter { _.getInstances != 0 } | ||
val total = lst.iterator.map { e => e.getSize }.sum | ||
val str = lst.iterator | ||
.map { e => | ||
s"${e.getClassName}, ${e.getInstances}, ${e.getSize}, ${e.getInstances * 1.0 / nodecount}, ${e.getSize * 1.0 / e.getInstances}, ${e.getSize * 1.0 / nodecount}" | ||
} | ||
.mkString("Class, #instances, total size, instances/node, bytes/instance, bytes/node\n", "\n", "") | ||
(total, str) | ||
} | ||
|
||
def printHisto(sect: String, box: MeasurementBox, nodecount: Int): Unit = { | ||
println( | ||
s"\n<=========\nRunning ${sect} in ${box.timeNanos} ns == ${box.timeNanos * 1e-6} ms == ${box.timeNanos * 1e-3 / nodecount} us/node and costing ${box.histo.getTotalMemory} bytes == ${box.histo.getTotalMemory * 1.0 / (1 << 20)} MB == ${box.histo.getTotalMemory * 1.0 / nodecount} bytes/node." | ||
) | ||
val top20 = modHisto(box.histo, 20, nodecount) | ||
println(s"Top 20 account for ${top20._1 * 100.0 / box.histo.getTotalMemory}%:") | ||
println(top20._2) | ||
println("=========>\n\n") | ||
} | ||
|
||
def main(args: Array[String]): Unit = { | ||
println( | ||
s"VM is version ${System.getProperty("java.runtime.version")} with max heap ${java.lang.Runtime.getRuntime.maxMemory >> 20} mb.\n\n" | ||
) | ||
val box = new MeasurementBox | ||
box.histo = new Histogramer().createHistogram() | ||
val cpgBox = measure { loadCopyFile(args(0)) } | ||
val nodecount = cpgBox.result.asInstanceOf[Cpg].graph.nodeCount() | ||
val callcount = cpgBox.result.asInstanceOf[Cpg].graph.nodeCount("CALL") | ||
val indexify = measure { makeIndices(cpgBox.result.asInstanceOf[Cpg]) } | ||
val touch1 = measure { touchGraph(cpgBox.result.asInstanceOf[Cpg]) } | ||
val touch2 = measure { touchGraph(cpgBox.result.asInstanceOf[Cpg]) } | ||
val close = measure { cpgBox.result.asInstanceOf[Cpg].close() } | ||
println(s"Graph with ${nodecount} nodes (${callcount} calls) and ${touch1.result} edges at ${args(0)}") | ||
val histoAfter = new Histogramer().createHistogram() | ||
box.histo = histoAfter.diff(box.histo) | ||
val free = measure { cpgBox.result = null } | ||
box.timeNanos = cpgBox.timeNanos + indexify.timeNanos + touch1.timeNanos + touch2.timeNanos + free.timeNanos | ||
printHisto("complete benchmark", box, nodecount) | ||
printHisto("copy and load cpg file", cpgBox, nodecount) | ||
printHisto("load/create indexes", indexify, nodecount) | ||
printHisto("count edges (force complete loading)", touch1, nodecount) | ||
printHisto("count edges again", touch2, nodecount) | ||
printHisto("close graph", close, nodecount) | ||
printHisto("free memory", free, nodecount) | ||
|
||
println(s"VM details according to JOL: ${org.openjdk.jol.vm.VM.current().details()}. Layout of top consumers: \n\n") | ||
println( | ||
box.histo | ||
.iterator() | ||
.asScala | ||
.toBuffer | ||
.sortBy { e: HistogramEntry => -scala.math.abs(e.getSize) } | ||
.take(20) | ||
.flatMap { e => Try(Class.forName(e.getClassName)) match { case Success(v) => Some(v); case _ => None } } | ||
.iterator | ||
.map { c => | ||
org.openjdk.jol.info.ClassLayout.parseClass(c).toPrintable | ||
} | ||
.mkString("\n\n") | ||
) | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") | ||
|
||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") | ||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.8.1") |