Skip to content

Instantly share code, notes, and snippets.

@llimllib
Last active August 15, 2024 17:18
Show Gist options
  • Save llimllib/a98f5c185e096f6f6ec16c8172054930 to your computer and use it in GitHub Desktop.
Save llimllib/a98f5c185e096f6f6ec16c8172054930 to your computer and use it in GitHub Desktop.
$ ./test.sh
node version: v22.4.0
python version: Python 3.12.2
go version: go version go1.22.6 darwin/arm64
rg version: ripgrep 14.1.0
features:-simd-accel,+pcre2
simd(compile):+NEON
simd(runtime):+NEON
PCRE2 10.42 is available (JIT is available)
Benchmark 1: node /tmp/test.js
Time (mean ± σ): 401.8 ms ± 4.6 ms [User: 393.6 ms, System: 4.6 ms]
Range (min … max): 396.5 ms … 411.7 ms 10 runs
Benchmark 2: python /tmp/test.py
Time (mean ± σ): 317.9 ms ± 0.8 ms [User: 311.4 ms, System: 3.8 ms]
Range (min … max): 317.0 ms … 318.9 ms 10 runs
Benchmark 3: /tmp/gotest
Time (mean ± σ): 4.3 ms ± 12.6 ms [User: 1.1 ms, System: 0.8 ms]
Range (min … max): 1.9 ms … 78.8 ms 37 runs
Warning: Command took less than 5 ms to complete. Note that the results might be inaccurate because hyperfine can not calibrate the shell startup time much more precise than this limit. You can try to use the `-N`/`--shell=none` option to disable the shell completely.
Warning: The first benchmarking run for this command was significantly slower than the rest (78.8 ms). This could be caused by (filesystem) caches that were not filled until after the first run. You should consider using the '--warmup' option to fill those caches before the actual benchmark. Alternatively, use the '--prepare' option to clear the caches before each timing run.
Benchmark 4: rg '\w+@' /tmp/a || true
Time (mean ± σ): 6.0 ms ± 0.3 ms [User: 4.6 ms, System: 1.0 ms]
Range (min … max): 5.7 ms … 7.6 ms 356 runs
Summary
/tmp/gotest ran
1.42 ± 4.18 times faster than rg '\w+@' /tmp/a || true
74.51 ± 220.23 times faster than python /tmp/test.py
94.18 ± 278.37 times faster than node /tmp/test.js
#!/usr/bin/env bash
# 'hyperfine' is used to benchmark these programs:
# https://github.com/sharkdp/hyperfine
# brew install hyperfine
# create a test file at /tmp/a composed of 16,000 'a' characters, followed by a newline
printf 'a%.0s' {1..16000} > /tmp/a && printf '\n' >> /tmp/a
# set up the node program
printf "node version: %s\n" "$(node --version)"
printf "const { readFileSync } = require('node:fs')
readFileSync('/tmp/a').toString().match(new RegExp(/\w+@/))" > /tmp/test.js
# set up the python program. I believe that python's 'search' is equivalent to
# node's 'match', but it's possible that I'm wrong there. 'match' in python
# goes much faster, but only searches for a match of the entire string
printf "python version: %s\n" "$(python --version)"
printf "import re; re.search(r'\w+@',open('/tmp/a').read())" > /tmp/test.py
# set up the go program
printf "go version: %s\n" "$(go version)"
cat > /tmp/test.go << EOF
package main
import (
"fmt"
"os"
"regexp"
)
func main() {
pattern := regexp.MustCompile("\\\\w+@")
file, err := os.ReadFile("/tmp/a")
if err != nil {
fmt.Printf("Error opening file: %v\n", err)
return
}
pattern.FindAll(file, -1)
}
EOF
go build -o /tmp/gotest /tmp/test.go
printf "rg version: %s\n" "$(rg --version)"
# benchmark each in turn
# the '|| true' is because rg won't match the string and reports failure
hyperfine "node /tmp/test.js" "python /tmp/test.py" "/tmp/gotest" "rg '\w+@' /tmp/a || true"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment