<commit_before>package net.sandius.rembulan.test

import java.io.PrintStream
import java.util.Scanner

import net.sandius.rembulan.compiler.PrototypeCompilerChunkLoader
import net.sandius.rembulan.core.impl.DefaultLuaState
import net.sandius.rembulan.core.{Exec, LuaState, PreemptionContext, Table}
import net.sandius.rembulan.lib.LibUtils
import net.sandius.rembulan.lib.impl.{DefaultBasicLib, DefaultCoroutineLib, DefaultMathLib, DefaultStringLib}
import net.sandius.rembulan.parser.LuaCPrototypeReader

import scala.util.Try

object FannkuchRunner {

  def initEnv(state: LuaState, args: Seq[String]): Table = {
    val env = LibUtils.init(state, new DefaultBasicLib(new PrintStream(System.out)))
    env.rawset("coroutine", new DefaultCoroutineLib().installInto(state, state.newTable(0, 0)))
    env.rawset("math", new DefaultMathLib().installInto(state, state.newTable(0, 0)))
    env.rawset("string", new DefaultStringLib().installInto(state, state.newTable(0, 0)))

    // command-line arguments
    val argTable = state.newTable(0, 0)
    for ((a, i) <- args.zipWithIndex) {
      argTable.rawset(i + 1, a)
    }
    env.rawset("arg", argTable)

    env
  }

  def init(filename: String, args: String*): Exec = {
    val resourceStream = getClass.getResourceAsStream(filename)
    require (resourceStream != null, "resource must exist, is null")
    val sourceContents = new Scanner(resourceStream, "UTF-8").useDelimiter("\\A").next()
    require (sourceContents != null, "source contents must not be null")

    val luacName = "luac53"
    val preemptionContext = PreemptionContext.Never.INSTANCE
    val ldr = new PrototypeCompilerChunkLoader(new LuaCPrototypeReader(luacName), getClass.getClassLoader)

    val state = new DefaultLuaState.Builder()
        .withPreemptionContext(preemptionContext)
        .build()

    val env = initEnv(state, args)

    val func = ldr.loadTextChunk(state.newUpvalue(env), "fannkuch", sourceContents)

    val exec = new Exec(state)
    exec.init(func)
    exec
  }

  def timed[A](name: String)(body: => A): A = {
    val before = System.nanoTime()
    val result = body
    val after = System.nanoTime()

    val totalTimeMillis = (after - before) / 1000000.0
    println("%s took %.1f ms".format(name, totalTimeMillis))
    result
  }

  def doFile(prefix: String, filename: String, args: String*): Unit = {
    val exec = timed (prefix + "init") {
      init(filename, args:_*)
    }
    timed (prefix + "execution") {
      while (exec.isPaused) {
        exec.resume()
      }
    }
  }

  private def intProperty(key: String, default: Int): Int = {
    Option(System.getProperty(key)) flatMap { s => Try(s.toInt).toOption } getOrElse default
  }

  def main(args: Array[String]): Unit = {
    val n = intProperty("n", 10)
    val numRuns = intProperty("numRuns", 3)

    println("n = " + n)
    println("numRuns = " + numRuns)

    for (i <- 1 to numRuns) {
      doFile(s"RUN #$i: ", "/fannkuchredux.lua", n.toString)
    }

  }

}<commit_msg>Using CountingPreemptionContext in the Fannkuch benchmark runner, and printing performance statistics.<commit_after>package net.sandius.rembulan.test

import java.io.PrintStream
import java.util.Scanner

import net.sandius.rembulan.compiler.PrototypeCompilerChunkLoader
import net.sandius.rembulan.core.impl.DefaultLuaState
import net.sandius.rembulan.core.{Exec, LuaState, PreemptionContext, Table}
import net.sandius.rembulan.lib.LibUtils
import net.sandius.rembulan.lib.impl.{DefaultBasicLib, DefaultCoroutineLib, DefaultMathLib, DefaultStringLib}
import net.sandius.rembulan.parser.LuaCPrototypeReader
import net.sandius.rembulan.test.FragmentExecTestSuite.CountingPreemptionContext

import scala.util.Try

object FannkuchRunner {

  def initEnv(state: LuaState, args: Seq[String]): Table = {
    val env = LibUtils.init(state, new DefaultBasicLib(new PrintStream(System.out)))
    env.rawset("coroutine", new DefaultCoroutineLib().installInto(state, state.newTable(0, 0)))
    env.rawset("math", new DefaultMathLib().installInto(state, state.newTable(0, 0)))
    env.rawset("string", new DefaultStringLib().installInto(state, state.newTable(0, 0)))

    // command-line arguments
    val argTable = state.newTable(0, 0)
    for ((a, i) <- args.zipWithIndex) {
      argTable.rawset(i + 1, a)
    }
    env.rawset("arg", argTable)

    env
  }

  def init(pc: PreemptionContext, filename: String, args: String*): Exec = {
    val resourceStream = getClass.getResourceAsStream(filename)
    require (resourceStream != null, "resource must exist, is null")
    val sourceContents = new Scanner(resourceStream, "UTF-8").useDelimiter("\\A").next()
    require (sourceContents != null, "source contents must not be null")

    val luacName = "luac53"
    val preemptionContext = pc
    val ldr = new PrototypeCompilerChunkLoader(new LuaCPrototypeReader(luacName), getClass.getClassLoader)

    val state = new DefaultLuaState.Builder()
        .withPreemptionContext(preemptionContext)
        .build()

    val env = initEnv(state, args)

    val func = ldr.loadTextChunk(state.newUpvalue(env), "fannkuch", sourceContents)

    val exec = new Exec(state)
    exec.init(func)
    exec
  }

  def timed[A](name: String)(body: => A): A = {
    val before = System.nanoTime()
    val result = body
    val after = System.nanoTime()

    val totalTimeMillis = (after - before) / 1000000.0
    println("%s took %.1f ms".format(name, totalTimeMillis))
    result
  }

  def doFile(prefix: String, filename: String, args: String*): Unit = {
    val stepSize = 1000000
    val pc = new CountingPreemptionContext(stepSize)

    val exec = timed (prefix + "init") {
      init(pc, filename, args:_*)
    }

    var steps = 0

    val before = System.nanoTime()
    while (exec.isPaused) {
      exec.resume()
      steps += 1
    }
    val after = System.nanoTime()

    val totalTimeMillis = (after - before) / 1000000.0
    val totalCPUUnitsSpent = pc.totalCost
    val avgTimePerCPUUnitNanos = (after - before).toDouble / totalCPUUnitsSpent.toDouble
    val avgCPUUnitsPerSecond = (1000000000.0 * totalCPUUnitsSpent) / (after - before)

    println(prefix + "Execution took %.1f ms".format(totalTimeMillis))
    println(prefix + "Total CPU cost: " + pc.totalCost + " LI")
    println(prefix)
    println(prefix + "Step size: " + stepSize + " LI")
    println(prefix + "Num of steps: " + steps)
    println(prefix + "Avg time per step: %.3f ms".format(totalTimeMillis / steps))
    println(prefix)
    println(prefix + "Avg time per unit: %.2f ns".format(avgTimePerCPUUnitNanos))
    println(prefix + "Avg units per second: %.1f LI/s".format(avgCPUUnitsPerSecond))
    println()
  }

  private def intProperty(key: String, default: Int): Int = {
    Option(System.getProperty(key)) flatMap { s => Try(s.toInt).toOption } getOrElse default
  }

  def main(args: Array[String]): Unit = {
    val n = intProperty("n", 10)
    val numRuns = intProperty("numRuns", 3)

    println("n = " + n)
    println("numRuns = " + numRuns)

    for (i <- 1 to numRuns) {
      doFile(s"#$i\t", "/fannkuchredux.lua", n.toString)
    }

  }

}
{'fraction_non_alphanumeric': 0.12721394433511385, 'fraction_numerical': 0.011526567332021367, 'mean_word_length': 3.7687667560321714, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7592463', 'n_tokens_mistral': 2447, 'n_tokens_neox': 2331, 'n_words': 532}