From 44abdadfd2b504405aaba8dc9555dce066e81d86 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:50:59 -0500 Subject: [PATCH] Make a benchmark --- .gitignore | 2 ++ bench.nu | 30 +++++++++++++++++ benchmarks/game.fred | 50 ++++++++++++++++++++++++++++ build.sbt | 2 ++ project/plugins.sbt | 1 + src/main/resources/runtime/runtime.h | 9 +++++ src/main/scala/fred/Compiler.scala | 2 ++ src/main/scala/fred/Parser.scala | 11 +++--- 8 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 bench.nu create mode 100644 benchmarks/game.fred diff --git a/.gitignore b/.gitignore index da62223..cd10ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ sbt-launch.jar /.worksheet/ a.out +bench.bin +fred.jar diff --git a/bench.nu b/bench.nu new file mode 100644 index 0000000..a717f03 --- /dev/null +++ b/bench.nu @@ -0,0 +1,30 @@ +const jar = "fred.jar" +const outExe = "bench.bin" + +export def run [ file: string, lazyMarkScan: bool ] { + let benchName = $file | path basename + if $lazyMarkScan { + java -jar $jar $file -o $outExe --lazy-mark-scan-only + } else { + java -jar $jar $file -o $outExe + } + let res = ( + ^$"./($outExe)" + | lines + | last + | parse "Time stamp counter diff: {tsc}, clock diff: {clock}" + | get 0) + { + name: $benchName, + tsc: $res.tsc, + clock: $res.clock, + lazyMarkScan: $lazyMarkScan + } +} + +export def run-all [ ] { + let files = (ls benchmarks).name + let myAlgo = $files | each { |file| run $file false } + let lazyOnly = $files | each { |file| run $file true } + $myAlgo ++ $lazyOnly +} diff --git a/benchmarks/game.fred b/benchmarks/game.fred new file mode 100644 index 0000000..66ab1f7 --- /dev/null +++ b/benchmarks/game.fred @@ -0,0 +1,50 @@ +data Player = Player { + mut friends: PlayerList, + store: Store +} + +data PlayerList + = PlayerNil {} + | PlayerCons { + player: Player, + next: PlayerList + } + +data Store = Store { datums: Data } + +data Data + = DataCons { + value: int, + // This is mut only so the compiler thinks there can be a cycle at runtime + mut next: Data + } + | DataNil {} + +fn createDummyData(length: int): Data = + if length == 0 then DataNil {} + else DataCons { value: length, next: createDummyData(length - 1) } + +fn createPlayers(n: int, store: Store): PlayerList = + if n == 0 then PlayerNil {} + else + let player = Player { friends: PlayerNil {}, store: store } in + let next = createPlayers(n - 1, store) in + set player.friends PlayerCons { player: player, next: next }; + player.friends + +fn pointlessLoop(iters: int): int = + if iters == 0 then 0 + else + createPlayers(1000, Store { datums: createDummyData(10000) }); + c("processAllPCRs();"); + pointlessLoop(iters - 1) + +fn main(): int = + printf("Starting game benchmark\n"); + c("float clockStart = (float) clock()/CLOCKS_PER_SEC;"); + c("u_int64_t tscStart = rdtscp();"); + pointlessLoop(5000); + c("printf( + \"Time stamp counter diff: %ld, clock diff: %lf\", + rdtscp() - tscStart, + ((float) clock()/CLOCKS_PER_SEC) - clockStart);") diff --git a/build.sbt b/build.sbt index 6cce5ba..84e6fcd 100644 --- a/build.sbt +++ b/build.sbt @@ -15,6 +15,8 @@ lazy val root = project scalacOptions ++= Seq("-Wunused:all", "-deprecation"), + assembly / assemblyOutputPath := file("./fred.jar"), + libraryDependencies ++= Seq( "org.typelevel" %% "cats-parse" % "0.3.9", "com.github.scopt" %% "scopt" % "4.1.0", diff --git a/project/plugins.sbt b/project/plugins.sbt index 72d5096..d97bf79 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,3 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") addSbtPlugin("com.siriusxm" % "sbt-snapshot4s" % "0.1.5") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0") diff --git a/src/main/resources/runtime/runtime.h b/src/main/resources/runtime/runtime.h index 050fcb8..4fa1557 100644 --- a/src/main/resources/runtime/runtime.h +++ b/src/main/resources/runtime/runtime.h @@ -4,6 +4,7 @@ #include #include +#include enum Color { @@ -186,3 +187,11 @@ void processAllPCRs() pcrBuckets = next; } } + +// From https://stackoverflow.com/a/14783909/11882002 +static inline u_int64_t rdtscp() { + u_int64_t rax,rdx; + u_int32_t aux; + asm volatile ( "rdtscp\n" : "=a" (rax), "=d" (rdx), "=c" (aux) : : ); + return (rdx << 32) + rax; +} diff --git a/src/main/scala/fred/Compiler.scala b/src/main/scala/fred/Compiler.scala index 4e8d988..54dfef5 100644 --- a/src/main/scala/fred/Compiler.scala +++ b/src/main/scala/fred/Compiler.scala @@ -86,6 +86,8 @@ object Compiler { if (settings.includeMemcheck) "-I /usr/include/valgrind" else "" assert(s"gcc $extraIncludes -o $outExe -x c -".run(io).exitValue() == 0) + System.err.println(s"Created executable at $outExe") + File(outExe).setExecutable(true) runtimeHeader.close() } diff --git a/src/main/scala/fred/Parser.scala b/src/main/scala/fred/Parser.scala index 2ba3196..051e725 100644 --- a/src/main/scala/fred/Parser.scala +++ b/src/main/scala/fred/Parser.scala @@ -15,7 +15,8 @@ object Parser { } private object ParserHelpers { - val comment = (P.string("//") *> P.repUntil0(P.anyChar.void, crlf | lf)).void + val comment = (P.string("//") *> P.repUntil0(P.anyChar.void, crlf | lf)) + .void val ws: P0[Unit] = (sp | crlf | lf | comment).rep0.void extension [A](p1: P[A]) @@ -71,10 +72,12 @@ object Parser { case (num, end) => IntLiteral(num.toInt, Span(end - num.length, end)) }.withContext("int literal") val stringLiteral: P[StringLiteral] = spanned( - P.char('"') *> ((P.char('\\') ~ P.anyChar) | (P.charWhere(_ != '"'))).rep + P.char('"') *> ((P.char('\\') *> P.anyChar) | (P.charWhere(_ != '"'))).rep .string <* P.char('"') - ).map { case Spanned(text, span) => StringLiteral(text, span) } - .withContext("str literal") + ).map { case Spanned(text, span) => + // TODO make this work with multiple backslashes and stuff + StringLiteral(text.replace("\\\"", "\"").replace(raw"\\", "\\"), span) + }.withContext("str literal") val literal = intLiteral | stringLiteral val parenExpr = inParens(expr).withContext("paren expr") val varRefOrFnCallOrCtorCall =