@@ -0,0 +1,444 @@ |
| | 1 | +#!/usr/bin/env bash |
| | 2 | +# |
| | 3 | +# FERP vs grep Benchmark Suite |
| | 4 | +# Comprehensive performance comparison |
| | 5 | +# |
| | 6 | +# Requires: bash 4+, bc, python3 (for timing) |
| | 7 | +# |
| | 8 | + |
| | 9 | +set -e |
| | 10 | + |
| | 11 | +# Colors for output |
| | 12 | +RED='\033[0;31m' |
| | 13 | +GREEN='\033[0;32m' |
| | 14 | +YELLOW='\033[1;33m' |
| | 15 | +BLUE='\033[0;34m' |
| | 16 | +CYAN='\033[0;36m' |
| | 17 | +BOLD='\033[1m' |
| | 18 | +NC='\033[0m' # No Color |
| | 19 | + |
| | 20 | +# Configuration |
| | 21 | +BENCH_DIR="/tmp/ferp_benchmark_$$" |
| | 22 | +FERP="./ferp" |
| | 23 | +GREP="grep" |
| | 24 | +RUNS=3 # Number of runs per benchmark (take median) |
| | 25 | + |
| | 26 | +# Test file sizes |
| | 27 | +SMALL_LINES=10000 # ~700KB |
| | 28 | +MEDIUM_LINES=100000 # ~7MB |
| | 29 | +LARGE_LINES=1000000 # ~70MB |
| | 30 | + |
| | 31 | +# Results storage (simple arrays for portability) |
| | 32 | +RESULT_NAMES=() |
| | 33 | +RESULT_FERP_TIMES=() |
| | 34 | +RESULT_GREP_TIMES=() |
| | 35 | + |
| | 36 | +#------------------------------------------------------------------------------ |
| | 37 | +# Utility Functions |
| | 38 | +#------------------------------------------------------------------------------ |
| | 39 | + |
| | 40 | +cleanup() { |
| | 41 | + echo -e "\n${CYAN}Cleaning up...${NC}" |
| | 42 | + rm -rf "$BENCH_DIR" |
| | 43 | +} |
| | 44 | + |
| | 45 | +trap cleanup EXIT |
| | 46 | + |
| | 47 | +die() { |
| | 48 | + echo -e "${RED}ERROR: $1${NC}" >&2 |
| | 49 | + exit 1 |
| | 50 | +} |
| | 51 | + |
| | 52 | +check_prerequisites() { |
| | 53 | + echo -e "${CYAN}Checking prerequisites...${NC}" |
| | 54 | + |
| | 55 | + # Check ferp exists |
| | 56 | + if [[ ! -x "$FERP" ]]; then |
| | 57 | + echo -e "${YELLOW}Building ferp (release mode)...${NC}" |
| | 58 | + make release >/dev/null 2>&1 || die "Failed to build ferp" |
| | 59 | + fi |
| | 60 | + |
| | 61 | + # Verify ferp works |
| | 62 | + echo "test" | $FERP "test" >/dev/null 2>&1 || die "ferp not working" |
| | 63 | + |
| | 64 | + # Check grep exists |
| | 65 | + command -v $GREP >/dev/null 2>&1 || die "grep not found" |
| | 66 | + |
| | 67 | + echo -e "${GREEN}Prerequisites OK${NC}" |
| | 68 | +} |
| | 69 | + |
| | 70 | +create_test_files() { |
| | 71 | + echo -e "\n${CYAN}Creating test files in $BENCH_DIR...${NC}" |
| | 72 | + mkdir -p "$BENCH_DIR" |
| | 73 | + |
| | 74 | + # File 1: English-like text (varied content) - use awk for speed |
| | 75 | + echo -e " Creating english text file ($LARGE_LINES lines)..." |
| | 76 | + awk -v n="$LARGE_LINES" 'BEGIN { |
| | 77 | + lines[0] = "The quick brown fox jumps over the lazy dog near the riverbank." |
| | 78 | + lines[1] = "Hello world, this is line number %d of the benchmark test file." |
| | 79 | + lines[2] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit sed do." |
| | 80 | + lines[3] = "Error: connection timeout after 30000ms on server node-%d." |
| | 81 | + lines[4] = "DEBUG [2024-01-15 10:23:45] Processing request id=%d status=pending" |
| | 82 | + lines[5] = "user@example.com logged in from 192.168.1.%d at 12:00:00" |
| | 83 | + lines[6] = "WARNING: disk usage at %d%% on /dev/sda1 partition" |
| | 84 | + lines[7] = "Function calculate_total(items=[1,2,3]) returned value=%d" |
| | 85 | + lines[8] = "The API endpoint /api/v2/users/%d responded with HTTP 200 OK" |
| | 86 | + lines[9] = "Configuration: max_threads=16, timeout=5000, retry_count=3" |
| | 87 | + for (i = 1; i <= n; i++) { |
| | 88 | + idx = i % 10 |
| | 89 | + if (idx == 0 || idx == 2 || idx == 9) { |
| | 90 | + print lines[idx] |
| | 91 | + } else if (idx == 3) { |
| | 92 | + printf lines[idx] "\n", i % 100 |
| | 93 | + } else if (idx == 5) { |
| | 94 | + printf lines[idx] "\n", i % 256 |
| | 95 | + } else if (idx == 6) { |
| | 96 | + printf lines[idx] "\n", 50 + (i % 50) |
| | 97 | + } else if (idx == 7) { |
| | 98 | + printf lines[idx] "\n", i * 42 |
| | 99 | + } else { |
| | 100 | + printf lines[idx] "\n", i |
| | 101 | + } |
| | 102 | + } |
| | 103 | + }' > "$BENCH_DIR/english_large.txt" |
| | 104 | + |
| | 105 | + # File 2: Log-like file (structured) |
| | 106 | + echo -e " Creating log file ($MEDIUM_LINES lines)..." |
| | 107 | + awk -v n="$MEDIUM_LINES" 'BEGIN { |
| | 108 | + levels[0] = "INFO"; levels[1] = "DEBUG"; levels[2] = "WARN"; levels[3] = "ERROR" |
| | 109 | + for (i = 1; i <= n; i++) { |
| | 110 | + day = 1 + (i % 28) |
| | 111 | + hour = i % 24 |
| | 112 | + min = i % 60 |
| | 113 | + sec = i % 60 |
| | 114 | + comp = i % 20 |
| | 115 | + printf "[2024-01-%02d %02d:%02d:%02d] %s: Message number %d from component-%d\n", \ |
| | 116 | + day, hour, min, sec, levels[i % 4], i, comp |
| | 117 | + } |
| | 118 | + }' > "$BENCH_DIR/logs_medium.txt" |
| | 119 | + |
| | 120 | + # File 3: Code-like file |
| | 121 | + echo -e " Creating code file ($MEDIUM_LINES lines)..." |
| | 122 | + awk -v n="$MEDIUM_LINES" 'BEGIN { |
| | 123 | + for (i = 1; i <= n; i++) { |
| | 124 | + idx = i % 8 |
| | 125 | + if (idx == 0) printf "function process_data_%d(input) {\n", i |
| | 126 | + else if (idx == 1) print " const result = input.map(x => x * 2);" |
| | 127 | + else if (idx == 2) print " if (result.length > 0) {" |
| | 128 | + else if (idx == 3) print " console.log(\"Processing:\", result);" |
| | 129 | + else if (idx == 4) print " return result.filter(x => x > 10);" |
| | 130 | + else if (idx == 5) print " }" |
| | 131 | + else if (idx == 6) print " return [];" |
| | 132 | + else print "}" |
| | 133 | + } |
| | 134 | + }' > "$BENCH_DIR/code_medium.txt" |
| | 135 | + |
| | 136 | + # File 4: CSV-like data |
| | 137 | + echo -e " Creating CSV file ($MEDIUM_LINES lines)..." |
| | 138 | + awk -v n="$MEDIUM_LINES" 'BEGIN { |
| | 139 | + print "id,name,email,score,timestamp" |
| | 140 | + srand() |
| | 141 | + for (i = 1; i <= n; i++) { |
| | 142 | + score = int(rand() * 100) |
| | 143 | + printf "%d,user_%d,user%d@domain%d.com,%d,%d\n", \ |
| | 144 | + i, i, i, i % 100, score, 1700000000 + i |
| | 145 | + } |
| | 146 | + }' > "$BENCH_DIR/data_medium.csv" |
| | 147 | + |
| | 148 | + # File 5: Small file for quick tests |
| | 149 | + echo -e " Creating small file ($SMALL_LINES lines)..." |
| | 150 | + head -n $SMALL_LINES "$BENCH_DIR/english_large.txt" > "$BENCH_DIR/english_small.txt" |
| | 151 | + |
| | 152 | + # Print file sizes |
| | 153 | + echo -e "\n${CYAN}Test files created:${NC}" |
| | 154 | + ls -lh "$BENCH_DIR"/*.txt "$BENCH_DIR"/*.csv 2>/dev/null | awk '{print " " $9 ": " $5}' |
| | 155 | +} |
| | 156 | + |
| | 157 | +#------------------------------------------------------------------------------ |
| | 158 | +# Benchmark Functions |
| | 159 | +#------------------------------------------------------------------------------ |
| | 160 | + |
| | 161 | +# Run a command multiple times and return median time |
| | 162 | +run_timed() { |
| | 163 | + local cmd="$1" |
| | 164 | + local times=() |
| | 165 | + |
| | 166 | + for i in $(seq 1 $RUNS); do |
| | 167 | + # Use /usr/bin/time for portable timing |
| | 168 | + local t=$( { time eval "$cmd" >/dev/null 2>&1; } 2>&1 | grep real | sed 's/real[[:space:]]*//' ) |
| | 169 | + # Convert to seconds (handles both 0m0.123s and 0.123 formats) |
| | 170 | + if [[ "$t" =~ ([0-9]+)m([0-9.]+)s ]]; then |
| | 171 | + local mins="${BASH_REMATCH[1]}" |
| | 172 | + local secs="${BASH_REMATCH[2]}" |
| | 173 | + t=$(echo "$mins * 60 + $secs" | bc -l) |
| | 174 | + elif [[ "$t" =~ ^[0-9.]+$ ]]; then |
| | 175 | + : # already in seconds |
| | 176 | + else |
| | 177 | + t="999" # Error case |
| | 178 | + fi |
| | 179 | + times+=("$t") |
| | 180 | + done |
| | 181 | + |
| | 182 | + # Return median (sort and take middle) |
| | 183 | + printf '%s\n' "${times[@]}" | sort -n | sed -n "$((($RUNS + 1) / 2))p" |
| | 184 | +} |
| | 185 | + |
| | 186 | +# Alternative timing using date (more portable) |
| | 187 | +run_timed_portable() { |
| | 188 | + local cmd="$1" |
| | 189 | + local times=() |
| | 190 | + |
| | 191 | + for i in $(seq 1 $RUNS); do |
| | 192 | + local start=$(python3 -c 'import time; print(time.time())' 2>/dev/null || date +%s.%N) |
| | 193 | + eval "$cmd" >/dev/null 2>&1 |
| | 194 | + local end=$(python3 -c 'import time; print(time.time())' 2>/dev/null || date +%s.%N) |
| | 195 | + local t=$(echo "$end - $start" | bc -l) |
| | 196 | + times+=("$t") |
| | 197 | + done |
| | 198 | + |
| | 199 | + # Return median |
| | 200 | + printf '%s\n' "${times[@]}" | sort -n | sed -n "$((($RUNS + 1) / 2))p" |
| | 201 | +} |
| | 202 | + |
| | 203 | +benchmark_pattern() { |
| | 204 | + local name="$1" |
| | 205 | + local file="$2" |
| | 206 | + local ferp_args="$3" |
| | 207 | + local grep_args="$4" |
| | 208 | + local pattern="$5" |
| | 209 | + |
| | 210 | + printf " %-35s" "$name" |
| | 211 | + |
| | 212 | + # Run ferp |
| | 213 | + local ferp_time=$(run_timed_portable "$FERP $ferp_args '$pattern' '$file'") |
| | 214 | + |
| | 215 | + # Run grep |
| | 216 | + local grep_time=$(run_timed_portable "$GREP $grep_args '$pattern' '$file'") |
| | 217 | + |
| | 218 | + # Calculate speedup |
| | 219 | + local speedup=$(echo "scale=2; $grep_time / $ferp_time" | bc -l 2>/dev/null || echo "N/A") |
| | 220 | + |
| | 221 | + # Store results |
| | 222 | + RESULT_NAMES+=("$name") |
| | 223 | + RESULT_FERP_TIMES+=("$ferp_time") |
| | 224 | + RESULT_GREP_TIMES+=("$grep_time") |
| | 225 | + |
| | 226 | + # Color-code the speedup |
| | 227 | + local color="$NC" |
| | 228 | + if (( $(echo "$speedup > 1.5" | bc -l) )); then |
| | 229 | + color="$GREEN" |
| | 230 | + elif (( $(echo "$speedup < 0.8" | bc -l) )); then |
| | 231 | + color="$RED" |
| | 232 | + fi |
| | 233 | + |
| | 234 | + printf "ferp: %6.3fs grep: %6.3fs ${color}%5.2fx${NC}\n" "$ferp_time" "$grep_time" "$speedup" |
| | 235 | +} |
| | 236 | + |
| | 237 | +#------------------------------------------------------------------------------ |
| | 238 | +# Benchmark Suites |
| | 239 | +#------------------------------------------------------------------------------ |
| | 240 | + |
| | 241 | +run_literal_benchmarks() { |
| | 242 | + echo -e "\n${BOLD}${BLUE}=== Literal String Matching ===${NC}" |
| | 243 | + local file="$BENCH_DIR/english_large.txt" |
| | 244 | + |
| | 245 | + benchmark_pattern "Simple word (hello)" "$file" "" "" "hello" |
| | 246 | + benchmark_pattern "Common word (the)" "$file" "" "" "the" |
| | 247 | + benchmark_pattern "Longer phrase (quick brown)" "$file" "" "" "quick brown" |
| | 248 | + benchmark_pattern "Case insensitive (-i hello)" "$file" "-i" "-i" "hello" |
| | 249 | + benchmark_pattern "Fixed string (-F hello)" "$file" "-F" "-F" "hello" |
| | 250 | + benchmark_pattern "Word boundary (-w the)" "$file" "-w" "-w" "the" |
| | 251 | +} |
| | 252 | + |
| | 253 | +run_regex_benchmarks() { |
| | 254 | + echo -e "\n${BOLD}${BLUE}=== Regular Expression Matching ===${NC}" |
| | 255 | + local file="$BENCH_DIR/english_large.txt" |
| | 256 | + |
| | 257 | + benchmark_pattern "Dot wildcard (h.llo)" "$file" "" "" "h.llo" |
| | 258 | + benchmark_pattern "Star quantifier (hel*o)" "$file" "" "" "hel*o" |
| | 259 | + benchmark_pattern "Character class ([a-z]+)" "$file" "-E" "-E" "[a-z]+" |
| | 260 | + benchmark_pattern "Mixed class ([a-zA-Z0-9]+)" "$file" "-E" "-E" "[a-zA-Z0-9]+" |
| | 261 | + benchmark_pattern "Digit class ([0-9]+)" "$file" "-E" "-E" "[0-9]+" |
| | 262 | + benchmark_pattern "Alternation (cat|dog|fox)" "$file" "-E" "-E" "cat|dog|fox" |
| | 263 | + benchmark_pattern "Optional (colou?r)" "$file" "-E" "-E" "colou?r" |
| | 264 | + benchmark_pattern "One or more (hel+o)" "$file" "-E" "-E" "hel+o" |
| | 265 | +} |
| | 266 | + |
| | 267 | +run_anchor_benchmarks() { |
| | 268 | + echo -e "\n${BOLD}${BLUE}=== Anchor Patterns ===${NC}" |
| | 269 | + local file="$BENCH_DIR/english_large.txt" |
| | 270 | + |
| | 271 | + benchmark_pattern "Start anchor (^The)" "$file" "" "" "^The" |
| | 272 | + benchmark_pattern "End anchor (\\.$)" "$file" "" "" '\.$' |
| | 273 | + benchmark_pattern "Both anchors (^The.*dog$)" "$file" "-E" "-E" "^The.*dog$" |
| | 274 | + benchmark_pattern "Word start (\\<quick)" "$file" "" "" '\<quick' |
| | 275 | + benchmark_pattern "Word end (fox\\>)" "$file" "" "" 'fox\>' |
| | 276 | +} |
| | 277 | + |
| | 278 | +run_log_benchmarks() { |
| | 279 | + echo -e "\n${BOLD}${BLUE}=== Log File Patterns ===${NC}" |
| | 280 | + local file="$BENCH_DIR/logs_medium.txt" |
| | 281 | + |
| | 282 | + benchmark_pattern "Log level (ERROR)" "$file" "" "" "ERROR" |
| | 283 | + benchmark_pattern "Log level (-i warn)" "$file" "-i" "-i" "warn" |
| | 284 | + benchmark_pattern "Timestamp pattern ([0-9]{2}:[0-9]{2})" "$file" "-E" "-E" "[0-9]{2}:[0-9]{2}" |
| | 285 | + benchmark_pattern "Component (component-[0-9]+)" "$file" "-E" "-E" "component-[0-9]+" |
| | 286 | + benchmark_pattern "Multiple levels (ERROR|WARN)" "$file" "-E" "-E" "ERROR|WARN" |
| | 287 | +} |
| | 288 | + |
| | 289 | +run_code_benchmarks() { |
| | 290 | + echo -e "\n${BOLD}${BLUE}=== Code Pattern Matching ===${NC}" |
| | 291 | + local file="$BENCH_DIR/code_medium.txt" |
| | 292 | + |
| | 293 | + benchmark_pattern "Function name (function)" "$file" "" "" "function" |
| | 294 | + benchmark_pattern "Variable (const|let|var)" "$file" "-E" "-E" "const|let|var" |
| | 295 | + benchmark_pattern "Return statement (return)" "$file" "" "" "return" |
| | 296 | + benchmark_pattern "Console log (console\\.log)" "$file" "-E" "-E" "console\\.log" |
| | 297 | +} |
| | 298 | + |
| | 299 | +run_csv_benchmarks() { |
| | 300 | + echo -e "\n${BOLD}${BLUE}=== CSV/Data Pattern Matching ===${NC}" |
| | 301 | + local file="$BENCH_DIR/data_medium.csv" |
| | 302 | + |
| | 303 | + benchmark_pattern "Email pattern (@.*\\.com)" "$file" "-E" "-E" "@.*\\.com" |
| | 304 | + benchmark_pattern "Specific domain (domain50)" "$file" "" "" "domain50" |
| | 305 | + benchmark_pattern "User pattern (user_[0-9]+)" "$file" "-E" "-E" "user_[0-9]+" |
| | 306 | + benchmark_pattern "High score (,[89][0-9],)" "$file" "-E" "-E" ",[89][0-9]," |
| | 307 | +} |
| | 308 | + |
| | 309 | +run_special_benchmarks() { |
| | 310 | + echo -e "\n${BOLD}${BLUE}=== Special Cases ===${NC}" |
| | 311 | + local file="$BENCH_DIR/english_large.txt" |
| | 312 | + |
| | 313 | + benchmark_pattern "Invert match (-v error)" "$file" "-v" "-v" "error" |
| | 314 | + benchmark_pattern "Count only (-c the)" "$file" "-c" "-c" "the" |
| | 315 | + benchmark_pattern "Line number (-n hello)" "$file" "-n" "-n" "hello" |
| | 316 | + benchmark_pattern "Multiple patterns (cat|dog|bird|fish)" "$file" "-E" "-E" "cat|dog|bird|fish" |
| | 317 | + benchmark_pattern "Long alternation (the|and|for|with|from)" "$file" "-E" "-E" "the|and|for|with|from" |
| | 318 | +} |
| | 319 | + |
| | 320 | +run_scaling_benchmarks() { |
| | 321 | + echo -e "\n${BOLD}${BLUE}=== Scaling Tests ===${NC}" |
| | 322 | + |
| | 323 | + echo -e " ${CYAN}Small file (~700KB):${NC}" |
| | 324 | + benchmark_pattern " [a-z]+ on small" "$BENCH_DIR/english_small.txt" "-E" "-E" "[a-z]+" |
| | 325 | + |
| | 326 | + echo -e " ${CYAN}Large file (~70MB):${NC}" |
| | 327 | + benchmark_pattern " [a-z]+ on large" "$BENCH_DIR/english_large.txt" "-E" "-E" "[a-z]+" |
| | 328 | + |
| | 329 | + # Calculate scaling factor (get last two results) |
| | 330 | + local num_results=${#RESULT_FERP_TIMES[@]} |
| | 331 | + local small_ferp="${RESULT_FERP_TIMES[$((num_results-2))]}" |
| | 332 | + local large_ferp="${RESULT_FERP_TIMES[$((num_results-1))]}" |
| | 333 | + local small_grep="${RESULT_GREP_TIMES[$((num_results-2))]}" |
| | 334 | + local large_grep="${RESULT_GREP_TIMES[$((num_results-1))]}" |
| | 335 | + |
| | 336 | + echo -e "\n ${CYAN}Scaling (large/small ratio):${NC}" |
| | 337 | + local ferp_scale=$(echo "scale=1; $large_ferp / $small_ferp" | bc -l 2>/dev/null || echo "N/A") |
| | 338 | + local grep_scale=$(echo "scale=1; $large_grep / $small_grep" | bc -l 2>/dev/null || echo "N/A") |
| | 339 | + echo -e " ferp: ${ferp_scale}x grep: ${grep_scale}x (lower is better for large files)" |
| | 340 | +} |
| | 341 | + |
| | 342 | +#------------------------------------------------------------------------------ |
| | 343 | +# Report Generation |
| | 344 | +#------------------------------------------------------------------------------ |
| | 345 | + |
| | 346 | +print_summary() { |
| | 347 | + echo -e "\n${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}" |
| | 348 | + echo -e "${BOLD}${BLUE} SUMMARY ${NC}" |
| | 349 | + echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}" |
| | 350 | + |
| | 351 | + local total_ferp=0 |
| | 352 | + local total_grep=0 |
| | 353 | + local wins_ferp=0 |
| | 354 | + local wins_grep=0 |
| | 355 | + local count=${#RESULT_NAMES[@]} |
| | 356 | + |
| | 357 | + for i in "${!RESULT_NAMES[@]}"; do |
| | 358 | + local ft="${RESULT_FERP_TIMES[$i]}" |
| | 359 | + local gt="${RESULT_GREP_TIMES[$i]}" |
| | 360 | + total_ferp=$(echo "$total_ferp + $ft" | bc -l) |
| | 361 | + total_grep=$(echo "$total_grep + $gt" | bc -l) |
| | 362 | + |
| | 363 | + if (( $(echo "$ft < $gt" | bc -l) )); then |
| | 364 | + wins_ferp=$((wins_ferp + 1)) |
| | 365 | + else |
| | 366 | + wins_grep=$((wins_grep + 1)) |
| | 367 | + fi |
| | 368 | + done |
| | 369 | + |
| | 370 | + local avg_speedup=$(echo "scale=2; $total_grep / $total_ferp" | bc -l 2>/dev/null || echo "N/A") |
| | 371 | + |
| | 372 | + echo -e "\n${CYAN}Overall Statistics:${NC}" |
| | 373 | + echo -e " Total benchmarks run: $count" |
| | 374 | + echo -e " ferp wins: ${GREEN}$wins_ferp${NC}" |
| | 375 | + echo -e " grep wins: ${RED}$wins_grep${NC}" |
| | 376 | + printf " Total time - ferp: %.3fs grep: %.3fs\n" "$total_ferp" "$total_grep" |
| | 377 | + echo -e " ${BOLD}Average speedup: ${GREEN}${avg_speedup}x${NC}" |
| | 378 | + |
| | 379 | + echo -e "\n${CYAN}System Information:${NC}" |
| | 380 | + echo -e " OS: $(uname -s) $(uname -r)" |
| | 381 | + echo -e " CPU: $(sysctl -n machdep.cpu.brand_string 2>/dev/null || lscpu 2>/dev/null | grep 'Model name' | cut -d: -f2 | xargs || echo 'Unknown')" |
| | 382 | + echo -e " ferp version: $($FERP --version 2>&1 | head -1 || echo 'Unknown')" |
| | 383 | + echo -e " grep version: $($GREP --version 2>&1 | head -1 || echo 'Unknown')" |
| | 384 | + echo -e " Runs per benchmark: $RUNS (median taken)" |
| | 385 | + |
| | 386 | + echo -e "\n${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}" |
| | 387 | +} |
| | 388 | + |
| | 389 | +#------------------------------------------------------------------------------ |
| | 390 | +# Main |
| | 391 | +#------------------------------------------------------------------------------ |
| | 392 | + |
| | 393 | +main() { |
| | 394 | + echo -e "${BOLD}${BLUE}" |
| | 395 | + echo "╔══════════════════════════════════════════════════════════════╗" |
| | 396 | + echo "║ FERP vs grep Benchmark Suite ║" |
| | 397 | + echo "║ Comprehensive Performance Comparison ║" |
| | 398 | + echo "╚══════════════════════════════════════════════════════════════╝" |
| | 399 | + echo -e "${NC}" |
| | 400 | + |
| | 401 | + check_prerequisites |
| | 402 | + create_test_files |
| | 403 | + |
| | 404 | + echo -e "\n${BOLD}${CYAN}Running benchmarks (${RUNS} runs each, reporting median)...${NC}" |
| | 405 | + echo -e "${CYAN}Format: ferp time | grep time | speedup (>1 = ferp faster)${NC}\n" |
| | 406 | + |
| | 407 | + run_literal_benchmarks |
| | 408 | + run_regex_benchmarks |
| | 409 | + run_anchor_benchmarks |
| | 410 | + run_log_benchmarks |
| | 411 | + run_code_benchmarks |
| | 412 | + run_csv_benchmarks |
| | 413 | + run_special_benchmarks |
| | 414 | + run_scaling_benchmarks |
| | 415 | + |
| | 416 | + print_summary |
| | 417 | + |
| | 418 | + echo -e "\n${GREEN}Benchmark complete!${NC}" |
| | 419 | +} |
| | 420 | + |
| | 421 | +# Run with optional arguments |
| | 422 | +if [[ "$1" == "-h" || "$1" == "--help" ]]; then |
| | 423 | + echo "Usage: $0 [OPTIONS]" |
| | 424 | + echo "" |
| | 425 | + echo "Options:" |
| | 426 | + echo " -r, --runs N Number of runs per benchmark (default: 3)" |
| | 427 | + echo " -q, --quick Quick mode (smaller files, fewer runs)" |
| | 428 | + echo " -h, --help Show this help" |
| | 429 | + exit 0 |
| | 430 | +fi |
| | 431 | + |
| | 432 | +if [[ "$1" == "-q" || "$1" == "--quick" ]]; then |
| | 433 | + RUNS=1 |
| | 434 | + SMALL_LINES=1000 |
| | 435 | + MEDIUM_LINES=10000 |
| | 436 | + LARGE_LINES=100000 |
| | 437 | + echo -e "${YELLOW}Quick mode: reduced file sizes and single run${NC}" |
| | 438 | +fi |
| | 439 | + |
| | 440 | +if [[ "$1" == "-r" || "$1" == "--runs" ]]; then |
| | 441 | + RUNS="${2:-3}" |
| | 442 | +fi |
| | 443 | + |
| | 444 | +main |