Files
FastSyncAi/compare_rsync.sh
T
Theo Tappe bacb61f6ec Fix: Add missing checksum field to file_meta_t and file_task_t structs
The file_meta_t and file_task_t structs were missing the checksum field
that was being used in both client and server code, causing a compilation
error. Added uint64_t checksum to both structs and updated wq_push
declaration to match implementation.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-24 12:40:23 +02:00

364 lines
15 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# compare_rsync.sh — Compare fastSyncAI performance against rsync
#
# Usage:
# ./compare_rsync.sh [DATA_SIZE_MB] [RUN_COUNT]
#
# Examples:
# ./compare_rsync.sh # 100 MB, 3 runs (default)
# ./compare_rsync.sh 500 # 500 MB, 3 runs
# ./compare_rsync.sh 100 5 # 100 MB, 5 runs
#
# Tests:
# - Small files (many files, 256KB each)
# - Medium files (fewer files, 1-5MB each)
# - Large single file
# - Mixed directory structure
#
# Compares: fastSyncAI (TCP, multi-connection) vs rsync vs rsync with compression
# =============================================================================
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────────
DATA_SIZE_MB=${1:-100}
RUN_COUNT=${2:-3}
PORT=19199
HOST="127.0.0.1"
SERVER_BIN="./fastsync_server"
CLIENT_BIN="./fastsync_client"
# ── Directories ────────────────────────────────────────────────────────────
TEST_DIR="compare_test_$$"
SRC_DIR="$TEST_DIR/src"
DST_DIR_BASE="$TEST_DIR/dst"
# ── Colours ────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
# ── Helpers ────────────────────────────────────────────────────────────────
die() { echo -e "${RED}ERROR: $*${RESET}" >&2; exit 1; }
info() { echo -e "${CYAN}$*${RESET}"; }
ok() { echo -e "${GREEN}$*${RESET}"; }
warn() { echo -e "${YELLOW}$*${RESET}"; }
# ── Sanity checks ──────────────────────────────────────────────────────────
[ -x "$SERVER_BIN" ] || die "Server binary not found: $SERVER_BIN — run 'make' first."
[ -x "$CLIENT_BIN" ] || die "Client binary not found: $CLIENT_BIN — run 'make' first."
command -v rsync >/dev/null 2>&1 || die "rsync not found. Install rsync first."
cleanup() {
pkill -f "fastsync_server" 2>/dev/null || true
pkill -f "fastsync_client" 2>/dev/null || true
rm -rf "$TEST_DIR"
}
trap cleanup EXIT
# ── Generate test data ──────────────────────────────────────────────────────
generate_test_data() {
info "Generating ~${DATA_SIZE_MB}MB test data..."
rm -rf "$TEST_DIR"
mkdir -p "$SRC_DIR/small" "$SRC_DIR/medium" "$SRC_DIR/large"
# Small files: many small files to test overhead
# ~25% of total size in 256KB files
local small_count=$(( (DATA_SIZE_MB * 1048576 / 4) / 262144 ))
if [ "$small_count" -lt 1 ]; then small_count=10; fi
if [ "$small_count" -gt 100 ]; then small_count=100; fi
info " Creating $small_count small files (256KB each)..."
for i in $(seq 1 $small_count); do
dd if=/dev/urandom bs=256K count=1 of="$SRC_DIR/small/file_$i.bin" 2>/dev/null
done
# Medium files: 1MB each
local medium_count=$(( (DATA_SIZE_MB * 1048576 / 2) / 1048576 ))
if [ "$medium_count" -lt 1 ]; then medium_count=5; fi
if [ "$medium_count" -gt 20 ]; then medium_count=20; fi
info " Creating $medium_count medium files (1MB each)..."
for i in $(seq 1 $medium_count); do
dd if=/dev/urandom bs=1M count=1 of="$SRC_DIR/medium/file_$i.bin" 2>/dev/null
done
# Large files: remaining size in fewer large files
local large_size=$(( DATA_SIZE_MB - (small_count * 256 / 1024) - medium_count ))
if [ "$large_size" -lt 5 ]; then large_size=5; fi
local large_count=3
if [ "$large_size" -lt 10 ]; then large_count=1; fi
info " Creating $large_count large files (~${large_size}MB total)..."
for i in $(seq 1 $large_count); do
local this_size=$(( large_size * 1048576 / large_count / 1048576 ))
if [ "$this_size" -lt 1 ]; then this_size=1; fi
dd if=/dev/urandom bs=1M count=$this_size of="$SRC_DIR/large/file_$i.bin" 2>/dev/null
done
local total_files total_size
total_files=$(find "$SRC_DIR" -type f | wc -l)
total_size=$(du -sh "$SRC_DIR" | cut -f1)
ok "Generated $total_files files, $total_size total in $SRC_DIR"
}
# ── Timing helper ───────────────────────────────────────────────────────────
# Returns time in milliseconds
run_with_timer() {
local start end
start=$(date +%s%N)
"$@" > /dev/null 2>&1
end=$(date +%s%N)
local elapsed_ms=$(( (end - start) / 1000000 ))
echo $elapsed_ms
}
# ── Run fastSyncAI ──────────────────────────────────────────────────────────
run_fastsync() {
local dst_dir="$DST_DIR_BASE/fastsync_$$"
mkdir -p "$dst_dir"
# Clean destination
rm -rf "$dst_dir"/*
# Start server in background
"$SERVER_BIN" -p "$PORT" -d "$dst_dir" > /dev/null 2>&1 &
local srv_pid=$!
sleep 0.2
# Run client and capture output
local client_output
client_output=$(timeout 120 "$CLIENT_BIN" -h "$HOST" -p "$PORT" -s "$SRC_DIR" -n 4 2>&1) || true
# Wait for server to finish writing all files
sleep 3
# Clean up server
kill "$srv_pid" 2>/dev/null || true
wait "$srv_pid" 2>/dev/null || true
# Force sync to ensure all disk writes are complete before verification
sync
# Extract throughput from BENCH line
local ms mbs
ms=$(echo "$client_output" | grep "^BENCH:" | grep -oP 'ms=\K[0-9.]+' || echo "0")
mbs=$(echo "$client_output" | grep "^BENCH:" | grep -oP 'throughput_mbs=\K[0-9.]+' || echo "0")
# Verify files
if diff <(cd "$SRC_DIR" && find . -type f -exec md5sum {} \; | sort) \
<(cd "$dst_dir" && find . -type f -exec md5sum {} \; | sort) > /dev/null 2>&1; then
echo "${ms}|${mbs}|1"
else
echo "${ms}|${mbs}|0"
fi
# Cleanup
rm -rf "$dst_dir"
}
# ── Run rsync ───────────────────────────────────────────────────────────────
run_rsync() {
local use_compress=${1:-0}
local dst_dir="$DST_DIR_BASE/rsync_$$"
mkdir -p "$dst_dir"
# Clean destination
rm -rf "$dst_dir"/*
local start end
start=$(date +%s%N)
if [ "$use_compress" -eq 1 ]; then
rsync -a --compress --progress "$SRC_DIR/" "$dst_dir/" > /dev/null 2>&1
else
rsync -a --progress "$SRC_DIR/" "$dst_dir/" > /dev/null 2>&1
fi
end=$(date +%s%N)
local elapsed_ms=$(( (end - start) / 1000000 ))
# Calculate throughput
local total_bytes
total_bytes=$(find "$SRC_DIR" -type f -exec stat -c %s {} \; | awk '{sum+=$1} END {print sum}')
local mbs=0
if [ "$elapsed_ms" -gt 0 ]; then
mbs=$(awk "BEGIN {printf \"%.2f\", $total_bytes / 1048576.0 / ($elapsed_ms / 1000.0)}")
fi
# Verify files
local verified=1
if ! diff <(cd "$SRC_DIR" && find . -type f -exec md5sum {} \; | sort) \
<(cd "$dst_dir" && find . -type f -exec md5sum {} \; | sort) > /dev/null 2>&1; then
verified=0
fi
echo "${elapsed_ms}|${mbs}|${verified}"
# Cleanup
rm -rf "$dst_dir"
}
# ── Run comparison ──────────────────────────────────────────────────────────
run_comparison() {
local run=$1
info "Run $run/$RUN_COUNT..."
# Generate fresh data for each run
generate_test_data
# Test fastSyncAI
echo -n " Testing fastSyncAI... "
local fs_result
fs_result=$(run_fastsync)
IFS='|' read -r fs_ms fs_mbs fs_verified <<< "$fs_result"
if [ "$fs_verified" -eq 1 ]; then
ok "${fs_mbs} MB/s (${fs_ms}ms)"
else
warn "${fs_mbs} MB/s (${fs_ms}ms) - VERIFICATION FAILED"
fi
# Test rsync (no compression)
echo -n " Testing rsync... "
local rs_result
rs_result=$(run_rsync 0)
IFS='|' read -r rs_ms rs_mbs rs_verified <<< "$rs_result"
if [ "$rs_verified" -eq 1 ]; then
ok "${rs_mbs} MB/s (${rs_ms}ms)"
else
warn "${rs_mbs} MB/s (${rs_ms}ms) - VERIFICATION FAILED"
fi
# Test rsync with compression
echo -n " Testing rsync + compress... "
local rsc_result
rsc_result=$(run_rsync 1)
IFS='|' read -r rsc_ms rsc_mbs rsc_verified <<< "$rsc_result"
if [ "$rsc_verified" -eq 1 ]; then
ok "${rsc_mbs} MB/s (${rsc_ms}ms)"
else
warn "${rsc_mbs} MB/s (${rsc_ms}ms) - VERIFICATION FAILED"
fi
echo ""
echo " === Run $run Results ==="
printf " %-20s %-10s %-10s %s\n" "Tool" "Time (ms)" "MB/s" "Verified"
printf " %-20s %-10s %-10s %s\n" "--------------------" "----------" "----------" "---------"
printf " %-20s %-10s %-10s %s\n" "fastSyncAI" "$fs_ms" "$fs_mbs" "($([ "$fs_verified" -eq 1 ] && echo "✓" || echo "✗"))"
printf " %-20s %-10s %-10s %s\n" "rsync" "$rs_ms" "$rs_mbs" "($([ "$rs_verified" -eq 1 ] && echo "✓" || echo "✗"))"
printf " %-20s %-10s %-10s %s\n" "rsync+compress" "$rsc_ms" "$rsc_mbs" "($([ "$rsc_verified" -eq 1 ] && echo "✓" || echo "✗"))"
echo ""
# Return formatted results
echo "RUN:$run|fastSyncAI:${fs_ms}:${fs_mbs}:${fs_verified}|rsync:${rs_ms}:${rs_mbs}:${rs_verified}|rsync_c:${rsc_ms}:${rsc_mbs}:${rsc_verified}"
}
# ────────────────────────────────────────────────────────────────────────────
# MAIN
# ────────────────────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${RESET}"
echo -e "${BOLD}║ fastSyncAI vs rsync — Performance Comparison ║${RESET}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${RESET}"
echo ""
echo -e " Data size : ${CYAN}${DATA_SIZE_MB} MB${RESET}"
echo -e " Runs per test : ${CYAN}${RUN_COUNT}${RESET}"
echo -e " Server port : ${CYAN}${PORT}${RESET}"
echo ""
# Collect all results
declare -a ALL_RESULTS
for run in $(seq 1 $RUN_COUNT); do
result=$(run_comparison $run)
ALL_RESULTS+=("$result")
done
# ── Print summary ────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${RESET}"
echo -e "${BOLD} SUMMARY${RESET}"
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${RESET}"
echo ""
# Parse all results
declare -A FS_MS FS_MBS FS_VERIFIED
declare -A RS_MS RS_MBS RS_VERIFIED
declare -A RSC_MS RSC_MBS RSC_VERIFIED
for result in "${ALL_RESULTS[@]}"; do
# Extract run number
run_num=$(echo "$result" | grep -oP 'RUN:\K[0-9]+')
# Extract fastSyncAI
fs_data=$(echo "$result" | grep -oP 'fastSyncAI:\K[^|]+')
IFS=':' read -r fs_ms fs_mbs fs_v <<< "$fs_data"
FS_MS[$run_num]="$fs_ms"
FS_MBS[$run_num]="$fs_mbs"
FS_VERIFIED[$run_num]="$fs_v"
# Extract rsync
rs_data=$(echo "$result" | grep -oP 'rsync:\K[^|]+')
IFS=':' read -r rs_ms rs_mbs rs_v <<< "$rs_data"
RS_MS[$run_num]="$rs_ms"
RS_MBS[$run_num]="$rs_mbs"
RS_VERIFIED[$run_num]="$rs_v"
# Extract rsync+compress
rsc_data=$(echo "$result" | grep -oP 'rsync_c:\K[^|]+')
IFS=':' read -r rsc_ms rsc_mbs rsc_v <<< "$rsc_data"
RSC_MS[$run_num]="$rsc_ms"
RSC_MBS[$run_num]="$rsc_mbs"
RSC_VERIFIED[$run_num]="$rsc_v"
done
# Calculate averages
calc_avg() {
local -n arr=$1
local sum=0 count=0
for val in "${arr[@]}"; do
sum=$(awk "BEGIN {print $sum + $val}")
count=$((count + 1))
done
if [ "$count" -gt 0 ]; then
awk "BEGIN {printf \"%.1f\", $sum / $count}"
else
echo "0"
fi
}
fs_avg_ms=$(calc_avg FS_MS)
fs_avg_mbs=$(calc_avg FS_MBS)
rs_avg_ms=$(calc_avg RS_MS)
rs_avg_mbs=$(calc_avg RS_MBS)
rsc_avg_ms=$(calc_avg RSC_MS)
rsc_avg_mbs=$(calc_avg RSC_MBS)
# Check all verified
all_fs_ok=1; for v in "${FS_VERIFIED[@]}"; do [ "$v" -ne 1 ] && all_fs_ok=0; done
all_rs_ok=1; for v in "${RS_VERIFIED[@]}"; do [ "$v" -ne 1 ] && all_rs_ok=0; done
all_rsc_ok=1; for v in "${RSC_VERIFIED[@]}"; do [ "$v" -ne 1 ] && all_rsc_ok=0; done
# Print averages
printf " %-20s %-10s %-10s %s\n" "Tool" "Avg Time (ms)" "Avg MB/s" "All OK"
printf " %-20s %-10s %-10s %s\n" "--------------------" "------------" "----------" "-------"
printf " %-20s %-10s %-10s %s\n" "fastSyncAI" "$fs_avg_ms" "$fs_avg_mbs" "($([ "$all_fs_ok" -eq 1 ] && echo "✓" || echo "✗"))"
printf " %-20s %-10s %-10s %s\n" "rsync" "$rs_avg_ms" "$rs_avg_mbs" "($([ "$all_rs_ok" -eq 1 ] && echo "✓" || echo "✗"))"
printf " %-20s %-10s %-10s %s\n" "rsync+compress" "$rsc_avg_ms" "$rsc_avg_mbs" "($([ "$all_rsc_ok" -eq 1 ] && echo "✓" || echo "✗"))"
# Speedup calculation
echo ""
if [ "$(awk "BEGIN {print ($rs_avg_mbs > 0) ? 1 : 0}")" = "1" ]; then
fs_speedup=$(awk "BEGIN {printf \"%.2f\", $fs_avg_mbs / ($rs_avg_mbs == 0 ? 0.01 : $rs_avg_mbs)}")
# Compare as strings to avoid floating point issues
if [ "$(awk "BEGIN {print ($fs_avg_mbs > $rs_avg_mbs) ? 1 : 0}")" = "1" ]; then
echo -e " fastSyncAI is ${GREEN}${fs_speedup}x faster than rsync${RESET}"
elif [ "$(awk "BEGIN {print ($rs_avg_mbs > $fs_avg_mbs) ? 1 : 0}")" = "1" ]; then
speedup_inv=$(awk "BEGIN {printf \"%.2f\", $rs_avg_mbs / ($fs_avg_mbs == 0 ? 0.01 : $fs_avg_mbs)}")
echo -e " rsync is ${GREEN}${speedup_inv}x faster than fastSyncAI${RESET}"
else
echo -e " Both tools performed similarly"
fi
fi
echo ""
ok "Comparison complete!"