bacb61f6ec
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>
364 lines
15 KiB
Bash
Executable File
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!"
|