From f4c66075e7c30ade430fa6857a8bc99db99c066f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Dec 2025 20:52:50 +0000 Subject: [PATCH] Add word count benchmark Add word_count benchmark that counts words and lines in generated text, a classic MapReduce-style task. Implementations for C, Python, JavaScript, Ruby, and Hemlock all produce consistent results (9 words per line * N lines). --- CLAUDE.md | 1 + benchmarks.json | 15 +++++++ benchmarks/word_count/word_count.c | 53 +++++++++++++++++++++++ benchmarks/word_count/word_count.hml | 64 ++++++++++++++++++++++++++++ benchmarks/word_count/word_count.js | 15 +++++++ benchmarks/word_count/word_count.py | 17 ++++++++ benchmarks/word_count/word_count.rb | 15 +++++++ run.sh | 7 ++- 8 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 benchmarks/word_count/word_count.c create mode 100644 benchmarks/word_count/word_count.hml create mode 100644 benchmarks/word_count/word_count.js create mode 100644 benchmarks/word_count/word_count.py create mode 100644 benchmarks/word_count/word_count.rb diff --git a/CLAUDE.md b/CLAUDE.md index 515936a..1f31670 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,7 @@ HEMLOCKC_BIN="../hemlock/hemlockc" ./run.sh - **primes_sieve** - Sieve of Eratosthenes (array access, loops) - **json_serialize** - JSON serialization (object creation, string building) - **json_deserialize** - JSON parsing (string parsing, object creation) +- **word_count** - Count words/lines in large text (classic MapReduce task) ## Notes diff --git a/benchmarks.json b/benchmarks.json index 288ad83..8cd2f95 100644 --- a/benchmarks.json +++ b/benchmarks.json @@ -19,6 +19,21 @@ "description": "Sieve of Eratosthenes", "default_input": 1000000, "quick_input": 100000 + }, + "json_serialize": { + "description": "JSON serialization", + "default_input": 100000, + "quick_input": 10000 + }, + "json_deserialize": { + "description": "JSON parsing", + "default_input": 100000, + "quick_input": 10000 + }, + "word_count": { + "description": "Count words/lines in large text", + "default_input": 100000, + "quick_input": 10000 } }, "languages": { diff --git a/benchmarks/word_count/word_count.c b/benchmarks/word_count/word_count.c new file mode 100644 index 0000000..5e0e4f8 --- /dev/null +++ b/benchmarks/word_count/word_count.c @@ -0,0 +1,53 @@ +// Word count benchmark - C +// Count words and lines in large generated text (classic MapReduce task) + +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int n = argc > 1 ? atoi(argv[1]) : 100000; + + const char *phrase = "the quick brown fox jumps over the lazy dog\n"; + size_t phrase_len = strlen(phrase); + + // Generate text: repeat phrase n times + size_t total_len = phrase_len * n; + char *text = malloc(total_len + 1); + if (!text) { + fprintf(stderr, "Memory allocation failed\n"); + return 1; + } + + char *ptr = text; + for (int i = 0; i < n; i++) { + memcpy(ptr, phrase, phrase_len); + ptr += phrase_len; + } + *ptr = '\0'; + + // Count words and lines + long words = 0; + long lines = 0; + int in_word = 0; + + for (size_t i = 0; i < total_len; i++) { + char c = text[i]; + if (c == '\n') { + lines++; + } + if (isspace((unsigned char)c)) { + in_word = 0; + } else { + if (!in_word) { + words++; + in_word = 1; + } + } + } + + printf("%ld %ld\n", words, lines); + free(text); + return 0; +} diff --git a/benchmarks/word_count/word_count.hml b/benchmarks/word_count/word_count.hml new file mode 100644 index 0000000..95d8b85 --- /dev/null +++ b/benchmarks/word_count/word_count.hml @@ -0,0 +1,64 @@ +// Word count benchmark - Hemlock +// Count words and lines in large generated text (classic MapReduce task) + +fn parse_int(s) { + let val = 0; + let idx = 0; + while (idx < s.length) { + let ch = s[idx]; + let code: i32 = ch; + let zero: i32 = '0'; + val = val * 10 + (code - zero); + idx = idx + 1; + } + return val; +} + +fn is_whitespace(ch) { + let code: i32 = ch; + // space=32, tab=9, newline=10, carriage return=13 + return code == 32 || code == 9 || code == 10 || code == 13; +} + +let n = 100000; +if (args.length > 1) { + n = parse_int(args[1]); +} + +let phrase = "the quick brown fox jumps over the lazy dog\n"; + +// Generate text: repeat phrase n times +let text = ""; +let i = 0; +while (i < n) { + text = text + phrase; + i = i + 1; +} + +// Count words and lines +let words = 0; +let lines = 0; +let in_word = false; +let newline: i32 = '\n'; + +let j = 0; +while (j < text.length) { + let ch = text[j]; + let code: i32 = ch; + + if (code == newline) { + lines = lines + 1; + } + + if (is_whitespace(ch)) { + in_word = false; + } else { + if (!in_word) { + words = words + 1; + in_word = true; + } + } + j = j + 1; +} + +print(words + " " + lines); diff --git a/benchmarks/word_count/word_count.js b/benchmarks/word_count/word_count.js new file mode 100644 index 0000000..d86f5b1 --- /dev/null +++ b/benchmarks/word_count/word_count.js @@ -0,0 +1,15 @@ +// Word count benchmark - JavaScript +// Count words and lines in large generated text (classic MapReduce task) + +const n = parseInt(process.argv[2]) || 100000; + +const phrase = "the quick brown fox jumps over the lazy dog\n"; + +// Generate text: repeat phrase n times +const text = phrase.repeat(n); + +// Count words and lines +const words = text.split(/\s+/).filter(w => w.length > 0).length; +const lines = (text.match(/\n/g) || []).length; + +console.log(`${words} ${lines}`); diff --git a/benchmarks/word_count/word_count.py b/benchmarks/word_count/word_count.py new file mode 100644 index 0000000..3579506 --- /dev/null +++ b/benchmarks/word_count/word_count.py @@ -0,0 +1,17 @@ +# Word count benchmark - Python +# Count words and lines in large generated text (classic MapReduce task) + +import sys + +n = int(sys.argv[1]) if len(sys.argv) > 1 else 100000 + +phrase = "the quick brown fox jumps over the lazy dog\n" + +# Generate text: repeat phrase n times +text = phrase * n + +# Count words and lines +words = len(text.split()) +lines = text.count('\n') + +print(f"{words} {lines}") diff --git a/benchmarks/word_count/word_count.rb b/benchmarks/word_count/word_count.rb new file mode 100644 index 0000000..5daf67b --- /dev/null +++ b/benchmarks/word_count/word_count.rb @@ -0,0 +1,15 @@ +# Word count benchmark - Ruby +# Count words and lines in large generated text (classic MapReduce task) + +n = ARGV[0] ? ARGV[0].to_i : 100000 + +phrase = "the quick brown fox jumps over the lazy dog\n" + +# Generate text: repeat phrase n times +text = phrase * n + +# Count words and lines +words = text.split.length +lines = text.count("\n") + +puts "#{words} #{lines}" diff --git a/run.sh b/run.sh index 06bcb6f..a3c99d0 100755 --- a/run.sh +++ b/run.sh @@ -28,7 +28,7 @@ usage() { echo " --iter N Number of iterations (default: 3)" echo " --help, -h Show this help" echo "" - echo "Benchmarks: fib, array_sum, string_concat, primes_sieve, json_serialize, json_deserialize" + echo "Benchmarks: fib, array_sum, string_concat, primes_sieve, json_serialize, json_deserialize, word_count" echo " (leave empty to run all)" } @@ -78,6 +78,9 @@ get_input() { json_deserialize) [[ $QUICK_MODE -eq 1 ]] && echo 10000 || echo 100000 ;; + word_count) + [[ $QUICK_MODE -eq 1 ]] && echo 10000 || echo 100000 + ;; esac } @@ -201,7 +204,7 @@ run_all() { if [[ -n "$BENCHMARK" ]]; then benchmarks="$BENCHMARK" else - benchmarks="fib array_sum string_concat primes_sieve json_serialize json_deserialize" + benchmarks="fib array_sum string_concat primes_sieve json_serialize json_deserialize word_count" fi local languages="c hemlockc hemlock python javascript ruby"