// Developer tool: measure dictionary lookup on the real OpenCC data.
//
// It exists so that "the index made lookups fast enough" is a number in the
// repository rather than an opinion, and so regressions show up as a slower run.
// Two corpora are measured: the official golden input (dense Chinese) and a
// synthetic ASCII-heavy one (markup, code, logs), because they stress different
// parts of the engine.
//
// Usage:
// moon run tools/bench_lookup --target native
// moon run tools/bench_lookup --target native -- --input
///|
/// Read a text file, exiting with a message when it cannot be read.
fn read_text(path : String) -> String {
@fs.read_file_to_string(path) catch {
_ => {
println("bench_lookup: cannot read " + path)
@sys.exit(1)
""
}
}
}
///|
/// A synthetic ASCII-heavy corpus of about 30k code units.
fn ascii_corpus() -> String {
let out = StringBuilder()
let mut index = 0
while index < 400 {
out.write_string("The quick brown fox jumps over the lazy dog. 0123456789
\n",
)
index = index + 1
}
out.to_string()
}
///|
/// Run the chain until the clock is meaningful and report (passes, code units, ms).
///
/// `now()` has millisecond resolution, so a single pass over a small document
/// would read as 0 ms; the batch grows until the measurement is above 50 ms.
fn measure(chain : @core.Chain, text : String) -> (Int, Int, UInt64) {
let mut passes = 1
let mut elapsed : UInt64 = 0
let mut output = ""
while passes <= 4096 {
let start = @env.now()
let mut pass = 0
while pass < passes {
output = chain.apply(text)
pass = pass + 1
}
elapsed = @env.now() - start
if elapsed >= 50 {
break
}
passes = passes * 2
}
ignore(output)
(passes, text.length() * passes, elapsed)
}
///|
/// Entry point.
fn main {
let args = @env.args()
let mut input_path = "test/fixtures/golden/input/us_constitution_zhs.txt"
let mut index = 0
while index < args.length() && !args[index].has_prefix("--") {
index = index + 1
}
while index < args.length() {
let arg = args[index]
if arg == "--input" && index + 1 < args.length() {
input_path = args[index + 1]
index = index + 2
} else {
index = index + 1
}
}
let chinese = read_text(input_path)
let ascii = ascii_corpus()
let build_start = @env.now()
let phrases = @core.Dict::parse("STPhrases", @dict.st_phrases)
let characters = @core.Dict::parse("STCharacters", @dict.st_characters)
let build_ms = @env.now() - build_start
println(
"index build : " +
build_ms.to_string() +
" ms (" +
phrases.size().to_string() +
" + " +
characters.size().to_string() +
" entries)",
)
// Bucket statistics are the whole point of the index: they say what a single
// lookup actually has to scan.
let mut max_bucket = 0
let mut total = 0
for entry in phrases.buckets.iter() {
let length = entry.1.length()
total = total + length
if length > max_bucket {
max_bucket = length
}
}
let buckets = phrases.buckets.length()
println(
"buckets : " +
buckets.to_string() +
" first-code-unit groups, largest " +
max_bucket.to_string() +
", average " +
(total / buckets).to_string(),
)
let pass = @core.Node::group(@core.ShortCircuit, [
@core.Node::leaf(phrases),
@core.Node::leaf(characters),
])
let chain = @core.Chain::new("s2t-lite", [], None, [pass])
let (passes1, units1, elapsed1) = measure(chain, chinese)
println("")
println(
"corpus : Chinese (opencc golden input, " +
chinese.length().to_string() +
" code units)",
)
report(passes1, units1, elapsed1)
let (passes2, units2, elapsed2) = measure(chain, ascii)
println("")
println(
"corpus : ASCII-heavy (synthetic markup, " +
ascii.length().to_string() +
" code units)",
)
report(passes2, units2, elapsed2)
println("")
println(
"note : s2t-lite = short_circuit(STPhrases, STCharacters); the",
)
println(" official chain adds the CJK normalisation pass (F5).")
}
///|
/// Print one measurement line.
fn report(passes : Int, units : Int, elapsed : UInt64) -> Unit {
let milliseconds = elapsed.to_int() + 1
println(
"converted : " +
units.to_string() +
" code units over " +
passes.to_string() +
" pass(es) in " +
elapsed.to_string() +
" ms",
)
println(
"throughput : " + (units / milliseconds).to_string() + " code units/ms",
)
}