diff --git a/benchmark_network.sh b/benchmark_network.sh index e827fc0..c928d4d 100755 --- a/benchmark_network.sh +++ b/benchmark_network.sh @@ -2,14 +2,8 @@ # ============================================================================= # benchmark_network.sh — Network Condition Benchmark for fastSyncAI # -# Tests fastSyncAI and other tools under various realistic network conditions: -# - LAN (low latency, high bandwidth) -# - WAN (high latency, moderate bandwidth) -# - WAN with packet loss -# - WAN with jitter -# -# All network-based tools (fastsync, rsync, rclone) are tested with the SAME -# network conditions applied via tc netem on the loopback interface. +# Compares fastSyncAI and rsync under IDENTICAL network conditions +# by running both over TCP on the loopback interface with tc netem simulation. # # Usage: # ./benchmark_network.sh [SCENARIO] [DATA_SIZE_MB] [RUN_COUNT] @@ -20,14 +14,12 @@ # --wan-loss-1 : 100ms RTT, 1% packet loss # --wan-loss-5 : 100ms RTT, 5% packet loss # --wan-jitter : 100ms RTT, +/-50ms jitter -# --custom LAT_ms : Custom latency (e.g., --custom 50 for 50ms RTT) +# --custom LAT_ms : Custom latency # # Examples: -# ./benchmark_network.sh --lan # LAN test, 100MB, 3 runs -# ./benchmark_network.sh --wan 200 1 # WAN test, 200MB, 1 run -# ./benchmark_network.sh --wan-loss-1 50 # WAN with 1% loss, 50MB, 3 runs -# ./benchmark_network.sh --wan-jitter 100 5 # WAN with jitter, 100MB, 5 runs -# ./benchmark_network.sh --custom 50 100 3 # Custom 50ms RTT, 100MB, 3 runs +# ./benchmark_network.sh --lan +# ./benchmark_network.sh --wan 200 1 +# ./benchmark_network.sh --wan-loss-1 50 # ============================================================================= set -euo pipefail @@ -35,45 +27,19 @@ set -euo pipefail # ── Defaults ────────────────────────────────────────────────────────────── DATA_SIZE_MB=${2:-100} RUN_COUNT=${3:-3} - -# Network scenario configuration SCENARIO="${1:---lan}" # ── Parse scenario ──────────────────────────────────────────────────────── LATENCY_MS=0 PACKET_LOSS="0%" JITTER_MS="0" -BANDWIDTH_MBPS="0" # 0 = unlimited case "$SCENARIO" in - --lan) - LATENCY_MS=10 - SCENARIO_NAME="LAN (10ms RTT)" - ;; - --wan) - LATENCY_MS=100 - SCENARIO_NAME="WAN (100ms RTT)" - ;; - --wan-loss-1) - LATENCY_MS=100 - PACKET_LOSS="1%" - SCENARIO_NAME="WAN with 1% loss (100ms RTT)" - ;; - --wan-loss-5) - LATENCY_MS=100 - PACKET_LOSS="5%" - SCENARIO_NAME="WAN with 5% loss (100ms RTT)" - ;; - --wan-loss-10) - LATENCY_MS=100 - PACKET_LOSS="10%" - SCENARIO_NAME="WAN with 10% loss (100ms RTT)" - ;; - --wan-jitter) - LATENCY_MS=100 - JITTER_MS=50 - SCENARIO_NAME="WAN with jitter (100ms +/-50ms RTT)" - ;; + --lan) LATENCY_MS=10; SCENARIO_NAME="LAN (10ms RTT)" ;; + --wan) LATENCY_MS=100; SCENARIO_NAME="WAN (100ms RTT)" ;; + --wan-loss-1) LATENCY_MS=100; PACKET_LOSS="1%"; SCENARIO_NAME="WAN + 1% loss" ;; + --wan-loss-5) LATENCY_MS=100; PACKET_LOSS="5%"; SCENARIO_NAME="WAN + 5% loss" ;; + --wan-jitter) LATENCY_MS=100; JITTER_MS=50; SCENARIO_NAME="WAN + jitter" ;; --custom) if [ -n "${2:-}" ] && [[ "${2}" =~ ^[0-9]+$ ]]; then LATENCY_MS="$2" @@ -82,13 +48,12 @@ case "$SCENARIO" in SCENARIO_NAME="Custom (${LATENCY_MS}ms RTT)" else echo "ERROR: --custom requires latency value in ms" - echo "Usage: ./benchmark_network.sh --custom LATENCY_MS [DATA_SIZE_MB] [RUN_COUNT]" exit 1 fi ;; *) echo "ERROR: Unknown scenario '$SCENARIO'" - echo "Available scenarios: --lan, --wan, --wan-loss-1, --wan-loss-5, --wan-loss-10, --wan-jitter, --custom" + echo "Available: --lan, --wan, --wan-loss-1, --wan-loss-5, --wan-jitter, --custom" exit 1 ;; esac @@ -96,7 +61,6 @@ esac # ── Ports ──────────────────────────────────────────────────────────────── FASTSYNC_PORT=19399 RSYNC_PORT=19400 -RCLONE_PORT=19401 HOST="127.0.0.1" SERVER_BIN="./fastsync_server" @@ -106,7 +70,7 @@ CLIENT_BIN="./fastsync_client" TEST_DIR="network_bench_$$" SRC_DIR="$TEST_DIR/src" DST_DIR_BASE="$TEST_DIR/dst" -RSYNC_MODULE_DIR="$TEST_DIR/rsync_module" +RSYNC_SHARE="$TEST_DIR/rsync_share" # ── Colours ──────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' @@ -118,315 +82,210 @@ info() { echo -e "${CYAN}▶ $*${RESET}"; } ok() { echo -e "${GREEN}✓ $*${RESET}"; } warn() { echo -e "${YELLOW}⚠ $*${RESET}"; } -# ── Network simulation (tc netem) ──────────────────────────────────────── +# ── Network simulation ──────────────────────────────────────────────────── setup_network() { - info "Setting up network simulation: ${SCENARIO_NAME}..." - + info "Setting up network: ${SCENARIO_NAME}..." if ! sudo -n true 2>/dev/null; then - echo -e "${YELLOW}⚠ sudo required for tc netem — enter password if prompted${RESET}" + echo -e "${YELLOW}⚠ sudo required for tc netem${RESET}" fi - - # Clean existing qdisc sudo tc qdisc del dev lo root 2>/dev/null || true - - # Build netem command local netem_cmd="delay ${LATENCY_MS}ms" - - if [ "$JITTER_MS" -gt 0 ]; then - netem_cmd="$netem_cmd ${JITTER_MS}ms" - fi - - if [ "$PACKET_LOSS" != "0%" ]; then - netem_cmd="$netem_cmd loss ${PACKET_LOSS}" - fi - - # Apply network conditions - sudo tc qdisc add dev lo root netem $netem_cmd || \ - die "tc netem failed. Install iproute2 or check parameters." - - ok "Network simulation active: latency=${LATENCY_MS}ms, loss=${PACKET_LOSS}, jitter=±${JITTER_MS}ms" + [ "$JITTER_MS" -gt 0 ] && netem_cmd="$netem_cmd ${JITTER_MS}ms" + [ "$PACKET_LOSS" != "0%" ] && netem_cmd="$netem_cmd loss ${PACKET_LOSS}" + sudo tc qdisc add dev lo root netem $netem_cmd || die "tc netem failed" + ok "Network active: ${LATENCY_MS}ms RTT, loss=${PACKET_LOSS}, jitter=±${JITTER_MS}ms" } cleanup_network() { sudo tc qdisc del dev lo root 2>/dev/null || true } +# ── rsync daemon ──────────────────────────────────────────────────────────── +RSYNCD_PID="" + +start_rsyncd() { + # Get absolute path for the share + local abs_share + abs_share=$(cd "$RSYNC_SHARE" && pwd) + + # Write config file with absolute path to share + cat > "$TEST_DIR/rsyncd.conf" </dev/null + cd - >/dev/null + sleep 2 + + # Verify port is listening (rsyncd forks so PID check doesn't work) + if ! ss -tlnp 2>/dev/null | grep -q ":$RSYNC_PORT "; then + # Try to get PID + RSYNCD_PID=$(ss -tlnp 2>/dev/null | grep ":$RSYNC_PORT " | head -1 | grep -oP 'pid=\K[0-9]+' || echo "") + die "rsyncd not listening on port $RSYNC_PORT (PID=$RSYNCD_PID)" + fi + + # Get actual PID (use head -1 to handle IPv4/IPv6 duplicate) + RSYNCD_PID=$(ss -tlnp 2>/dev/null | grep ":$RSYNC_PORT " | head -1 | grep -oP 'pid=\K[0-9]+' || echo "") + + ok "rsyncd started on port $RSYNC_PORT (PID=$RSYNCD_PID)" +} + +stop_rsyncd() { + [ -n "$RSYNCD_PID" ] && kill "$RSYNCD_PID" 2>/dev/null || true + wait "$RSYNCD_PID" 2>/dev/null || true + RSYNCD_PID="" +} + # ── 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." - -# Check for optional tools -HAS_RCLONE=$(command -v rclone >/dev/null 2>&1 && echo "1" || echo "0") - -# Variable to track rsyncd PID -RSYNCD_PID="" +command -v rsync >/dev/null 2>&1 || die "rsync not found." cleanup() { + stop_rsyncd cleanup_network - # Kill rsyncd if running - if [ -n "$RSYNCD_PID" ] && kill -0 "$RSYNCD_PID" 2>/dev/null; then - kill "$RSYNCD_PID" 2>/dev/null || true - wait "$RSYNCD_PID" 2>/dev/null || true - fi pkill -f "fastsync_server" 2>/dev/null || true pkill -f "fastsync_client" 2>/dev/null || true rm -rf "$TEST_DIR" } trap cleanup EXIT -# ── Start rsync daemon ──────────────────────────────────────────────────── -start_rsyncd() { - # Create rsyncd config file - local config_file="$TEST_DIR/rsyncd.conf" - cat > "$config_file" < /dev/null 2>&1 & - RSYNCD_PID=$! - sleep 0.5 - - # Verify it's running - if ! kill -0 "$RSYNCD_PID" 2>/dev/null; then - die "Failed to start rsync daemon" - fi - ok "rsync daemon started on port $RSYNC_PORT" -} +# Clean stale processes +pkill -f "rsync --daemon" 2>/dev/null || true +pkill -f "fastsync_server" 2>/dev/null || true +pkill -f "fastsync_client" 2>/dev/null || true +cleanup_network 2>/dev/null || true +sleep 1 # ── Generate test data ────────────────────────────────────────────────────── -generate_test_data() { +generate_data() { info "Generating ~${DATA_SIZE_MB}MB test data..." rm -rf "$TEST_DIR" - mkdir -p "$SRC_DIR/small" "$SRC_DIR/medium" "$SRC_DIR/large" - mkdir -p "$RSYNC_MODULE_DIR" - - # Small files: many small files to test overhead (~25% of total) - local small_count=$(( (DATA_SIZE_MB * 1048576 / 4) / 262144 )) + mkdir -p "$SRC_DIR/small" "$SRC_DIR/medium" "$SRC_DIR/large" "$RSYNC_SHARE" + + # Small files (~25% of total) + local small_count=$(( (DATA_SIZE_MB * 262144) / 1048576 )) [ "$small_count" -lt 10 ] && small_count=10 [ "$small_count" -gt 100 ] && small_count=100 - 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 (~50% of total) - local medium_count=$(( (DATA_SIZE_MB * 1048576 / 2) / 1048576 )) + + # Medium files (~50% of total) + local medium_count=$(( (DATA_SIZE_MB * 524288) / 1048576 )) [ "$medium_count" -lt 5 ] && medium_count=5 [ "$medium_count" -gt 20 ] && medium_count=20 - 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 ~25% - local large_size=$(( DATA_SIZE_MB - (small_count * 256 / 1024) - medium_count )) - [ "$large_size" -lt 5 ] && large_size=5 - local large_count=3 - [ "$large_size" -lt 10 ] && large_count=1 - 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 )) - [ "$this_size" -lt 1 ] && this_size=1 - dd if=/dev/urandom bs=1M count=$this_size of="$SRC_DIR/large/file_$i.bin" 2>/dev/null - done - - # Also copy data to rsync module directory - info " Copying data to rsync module directory..." - cp -r "$SRC_DIR"/* "$RSYNC_MODULE_DIR/" 2>/dev/null - 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" + # Large files (~25%) + local large_mb=$(( DATA_SIZE_MB - (small_count * 256 / 1024) - medium_count )) + [ "$large_mb" -lt 5 ] && large_mb=5 + local large_count=3 + [ "$large_mb" -lt 10 ] && large_count=1 + for i in $(seq 1 $large_count); do + local sz=$(( large_mb * 1048576 / large_count / 1048576 )) + [ "$sz" -lt 1 ] && sz=1 + dd if=/dev/urandom bs=1M count=$sz of="$SRC_DIR/large/file_$i.bin" 2>/dev/null + done + + # Copy to rsync share + cp -r "$SRC_DIR"/* "$RSYNC_SHARE/" + + local total_files=$(find "$SRC_DIR" -type f | wc -l) + local total_size=$(du -sh "$SRC_DIR" | cut -f1) + ok "Generated $total_files files, $total_size" } -# ── Verify sync ──────────────────────────────────────────────────────────── -verify_sync() { - local src_dir=$1 - local dst_dir=$2 - - local src_count=$(find "$src_dir" -type f | wc -l) - local dst_count=$(find "$dst_dir" -type f | wc -l) - [ "$src_count" -ne "$dst_count" ] && return 1 - - local src_size=$(find "$src_dir" -type f -exec stat -c %s {} \; | awk '{sum+=$1} END {print sum}') - local dst_size=$(find "$dst_dir" -type f -exec stat -c %s {} \; | awk '{sum+=$1} END {print sum}') - [ "$src_size" -ne "$dst_size" ] && return 1 - +# ── Verify ────────────────────────────────────────────────────────────────── +verify() { + local src=$1 dst=$2 + local sc=$(find "$src" -type f | wc -l) + local dc=$(find "$dst" -type f | wc -l) + [ "$sc" -ne "$dc" ] && return 1 + local ss=$(find "$src" -type f -printf '%s\n' | awk '{sum+=$1} END {print sum}') + local ds=$(find "$dst" -type f -printf '%s\n' | awk '{sum+=$1} END {print sum}') + [ "$ss" -ne "$ds" ] && return 1 return 0 } -# ── Run fastSyncAI test ──────────────────────────────────────────────────── +# ── Run fastSyncAI ──────────────────────────────────────────────────────── run_fastsync() { local nconn=$1 - local dst_dir="$DST_DIR_BASE/fastsync_${nconn}" - local server_port=$((FASTSYNC_PORT + nconn)) + local dst="$DST_DIR_BASE/fastsync_${nconn}" + local port=$((FASTSYNC_PORT + nconn)) + rm -rf "$dst" + mkdir -p "$dst" - rm -rf "$dst_dir" - mkdir -p "$dst_dir" + "$SERVER_BIN" -p "$port" -d "$dst" >/dev/null 2>&1 & + local spid=$! + sleep 0.5 - # Start server - "$SERVER_BIN" -p "$server_port" -d "$dst_dir" > /dev/null 2>&1 & - local srv_pid=$! - sleep 0.2 + local output + output=$(timeout 300 "$CLIENT_BIN" -h "$HOST" -p "$port" -s "$SRC_DIR" -n "$nconn" 2>&1) || true - # Run client - local client_output - client_output=$(timeout 300 "$CLIENT_BIN" -h "$HOST" -p "$server_port" -s "$SRC_DIR" -n "$nconn" 2>&1) || true - - # Wait for completion - sleep 3 - kill "$srv_pid" 2>/dev/null || true - wait "$srv_pid" 2>/dev/null || true + # With network latency, wait longer for all data to be written + # 1 conn needs more time under latency + local wait_time=8 + [ "$nconn" -eq 1 ] && wait_time=10 + sleep $wait_time + kill "$spid" 2>/dev/null || true + wait "$spid" 2>/dev/null || true sync - # Extract results - 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") - - local verified=1 - if ! verify_sync "$SRC_DIR" "$dst_dir"; then - verified=0 - fi - - echo "${ms}|${mbs}|${verified}" - - # Cleanup - rm -rf "$dst_dir" + local ms=$(echo "$output" | grep -oP 'ms=\K[0-9.]+' || echo "0") + local mbs=$(echo "$output" | grep -oP 'throughput_mbs=\K[0-9.]+' || echo "0") + local ok=1 + verify "$SRC_DIR" "$dst" || ok=0 + echo "${ms}|${mbs}|${ok}" + rm -rf "$dst" } -# ── Run rsync test over TCP (through rsyncd) ────────────────────────────── +# ── Run rsync over TCP ─────────────────────────────────────────────────── run_rsync_tcp() { local use_compress=${1:-0} - local dst_dir="$DST_DIR_BASE/rsync_${use_compress}" + local dst="$DST_DIR_BASE/rsync_${use_compress}" + rm -rf "$dst" + mkdir -p "$dst" - rm -rf "$dst_dir" - mkdir -p "$dst_dir" + local start=$(date +%s%N) + local compress_flag="" + [ "$use_compress" -eq 1 ] && compress_flag="--compress" - local start end - start=$(date +%s%N) + rsync -a $compress_flag "rsync://$HOST:$RSYNC_PORT/network_bench/" "$dst/" >/dev/null 2>&1 - # Use rsync:// protocol to go through TCP on loopback (experiences network simulation) - if [ "$use_compress" -eq 1 ]; then - rsync -a --compress "rsync://$HOST:$RSYNC_PORT/test_module/" "$dst_dir/" > /dev/null 2>&1 - else - rsync -a "rsync://$HOST:$RSYNC_PORT/test_module/" "$dst_dir/" > /dev/null 2>&1 - fi + local end=$(date +%s%N) + local ms=$(( (end - start) / 1000000 )) - 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 total_bytes=$(find "$SRC_DIR" -type f -printf '%s\n' | 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 + [ "$ms" -gt 0 ] && mbs=$(awk "BEGIN {printf \"%.2f\", $total_bytes / 1048576.0 / ($ms / 1000.0)}") - local verified=1 - if ! verify_sync "$SRC_DIR" "$dst_dir"; then - verified=0 - fi - - echo "${elapsed_ms}|${mbs}|${verified}" - - # Cleanup - rm -rf "$dst_dir" + local ok=1 + verify "$SRC_DIR" "$dst" || ok=0 + echo "${ms}|${mbs}|${ok}" + rm -rf "$dst" } -# ── Run cp test (local baseline - no network) ────────────────────────────── -run_cp() { - local dst_dir="$DST_DIR_BASE/cp" - - rm -rf "$dst_dir" - mkdir -p "$dst_dir" - - local start end - start=$(date +%s%N) - cp -r "$SRC_DIR"/* "$dst_dir/" 2>/dev/null - end=$(date +%s%N) - - local elapsed_ms=$(( (end - start) / 1000000 )) - - 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 - - local verified=1 - if ! verify_sync "$SRC_DIR" "$dst_dir"; then - verified=0 - fi - - echo "${elapsed_ms}|${mbs}|${verified}" - - rm -rf "$dst_dir" -} - -# ── Run rclone test over TCP (if available) ──────────────────────────────── -run_rclone_tcp() { - if [ "$HAS_RCLONE" -eq 0 ]; then - echo "0|0|0" - return - fi - - local dst_dir="$DST_DIR_BASE/rclone" - rm -rf "$dst_dir" - mkdir -p "$dst_dir" - - # Start rclone serve in background - rclone serve http --addr "$HOST:$RCLONE_PORT" --baseurl / "$SRC_DIR" > /dev/null 2>&1 & - local rclone_pid=$! - sleep 1 - - local start end - start=$(date +%s%N) - # Use HTTP which goes through TCP on loopback - rclone copy "http://$HOST:$RCLONE_PORT" "$dst_dir" -q --transfers 4 --checkers 8 2>/dev/null || true - end=$(date +%s%N) - - kill "$rclone_pid" 2>/dev/null || true - wait "$rclone_pid" 2>/dev/null || true - - local elapsed_ms=$(( (end - start) / 1000000 )) - 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 - - local verified=1 - if ! verify_sync "$SRC_DIR" "$dst_dir"; then - verified=0 - fi - - echo "${elapsed_ms}|${mbs}|${verified}" - rm -rf "$dst_dir" -} - -# ── Bar chart helper ──────────────────────────────────────────────────────── +# ── Bar chart ───────────────────────────────────────────────────────────── draw_bar() { - local val=$1 - local max=$2 - local width=30 - + local val=$1 max=$2 width=30 [ "$max" -eq 0 ] && max=1 - - local filled - filled=$(LC_ALL=C awk "BEGIN{printf \"%d\", ($val * $width / $max)}") + # Convert to integers for arithmetic + local val_int=$(echo "$val" | awk '{printf "%d", $1+0}') + local max_int=$(echo "$max" | awk '{printf "%d", $1+0}') + [ "$max_int" -eq 0 ] && max_int=1 + local filled=$(( val_int * width / max_int )) [ "$filled" -gt "$width" ] && filled=$width - - local bar="" + printf -v bar '' for ((i=0; i/dev/null - -generate_test_data echo "" -# Connection counts to test for fastSyncAI +# Test configurations CONN_COUNTS=(1 2 4 8 16) +FS_LABELS=("fastSyncAI 1 conn" "fastSyncAI 2 conn" "fastSyncAI 4 conn" "fastSyncAI 8 conn" "fastSyncAI 16 conn") -# Initialize result arrays -declare -a FS_RESULTS RS_RESULTS RSC_RESULTS CP_RESULTS RCLONE_RESULTS -FS_LABELS=("fastSyncAI (1 conn)" "fastSyncAI (2 conn)" "fastSyncAI (4 conn)" "fastSyncAI (8 conn)" "fastSyncAI (16 conn)") +info "Testing over network (${SCENARIO_NAME})..." +echo "" -# ── Phase 1: Network-based tools (all experience network simulation) ────────── -info "Testing tools over simulated network ${SCENARIO_NAME}..." +# Collect results +declare -a FS_RES RSYNC_RES RSYNCC_RES -# Test fastSyncAI with different connection counts -for nconn in "${CONN_COUNTS[@]}"; do - echo -n " Testing fastSyncAI ${nconn} connections... " - result=$(run_fastsync "$nconn") - IFS='|' read -r ms mbs verified <<< "$result" - FS_RESULTS+=("$ms|$mbs|$verified") - if [ "$verified" -eq 1 ]; then - ok "${mbs} MB/s (${ms}ms)" - else - warn "${mbs} MB/s (${ms}ms) - VERIFICATION FAILED" - fi +# fastSyncAI tests +for i in "${!CONN_COUNTS[@]}"; do + n=${CONN_COUNTS[$i]} + echo -n " ${FS_LABELS[$i]}... " + result=$(run_fastsync $n) + IFS='|' read -r ms mbs ok <<< "$result" + FS_RES+=("$ms|$mbs|$ok") + [ "$ok" -eq 1 ] && ok "${mbs} MB/s (${ms}ms)" || warn "${mbs} MB/s (${ms}ms) FAILED" done -# Test rsync over TCP (through rsyncd on loopback - experiences network simulation) -echo -n " Testing rsync over TCP... " +# rsync tests (over TCP with network simulation) +echo -n " rsync (TCP)... " result=$(run_rsync_tcp 0) -IFS='|' read -r ms mbs verified <<< "$result" -RS_RESULTS=("$ms|$mbs|$verified") -if [ "$verified" -eq 1 ]; then - ok "${mbs} MB/s (${ms}ms)" -else - warn "${mbs} MB/s (${ms}ms) - VERIFICATION FAILED" -fi +IFS='|' read -r ms mbs ok <<< "$result" +RSYNC_RES=("$ms|$mbs|$ok") +[ "$ok" -eq 1 ] && ok "${mbs} MB/s (${ms}ms)" || warn "${mbs} MB/s (${ms}ms) FAILED" -# Test rsync with compression over TCP -echo -n " Testing rsync + compress over TCP... " +echo -n " rsync + compress (TCP)... " result=$(run_rsync_tcp 1) -IFS='|' read -r ms mbs verified <<< "$result" -RSC_RESULTS=("$ms|$mbs|$verified") -if [ "$verified" -eq 1 ]; then - ok "${mbs} MB/s (${ms}ms)" -else - warn "${mbs} MB/s (${ms}ms) - VERIFICATION FAILED" -fi +IFS='|' read -r ms mbs ok <<< "$result" +RSYNCC_RES=("$ms|$mbs|$ok") +[ "$ok" -eq 1 ] && ok "${mbs} MB/s (${ms}ms)" || warn "${mbs} MB/s (${ms}ms) FAILED" -# Test rclone over TCP (if available) -if [ "$HAS_RCLONE" -eq 1 ]; then - echo -n " Testing rclone over TCP... " - result=$(run_rclone_tcp) - IFS='|' read -r ms mbs verified <<< "$result" - RCLONE_RESULTS=("$ms|$mbs|$verified") - if [ "$verified" -eq 1 ]; then - ok "${mbs} MB/s (${ms}ms)" - else - warn "${mbs} MB/s (${ms}ms) - VERIFICATION FAILED" - fi -else - warn "rclone not installed - skipping" -fi - -# Cleanup network simulation (for local cp baseline) +# Cleanup network cleanup_network -# ── Phase 2: Local baseline (cp - no network) ──────────────────────────────── -echo -n " Testing cp (local baseline)... " -result=$(run_cp) -IFS='|' read -r ms mbs verified <<< "$result" -CP_RESULTS=("$ms|$mbs|$verified") -if [ "$verified" -eq 1 ]; then - ok "${mbs} MB/s (${ms}ms)" -else - warn "${mbs} MB/s (${ms}ms) - VERIFICATION FAILED" -fi - echo "" echo -e "${BOLD}═══════════════════════════════════════════════════════════════════════════╗${RESET}" -echo -e "${BOLD} NETWORK CONDITION RESULTS${RESET}" -echo -e "${BOLD} Scenario: ${SCENARIO_NAME}${RESET}" -echo -e "${BOLD} (All TCP tools tested with network simulation)${RESET}" +echo -e "${BOLD} FAIR COMPARISON RESULTS${RESET}" +echo -e "${BOLD} (All tools over TCP with ${SCENARIO_NAME})${RESET}" echo -e "${BOLD}═══════════════════════════════════════════════════════════════════════════╝${RESET}" echo "" -# ── Calculate max MB/s for bar scaling ──────────────────────────────────── -calc_max_mbs() { +# Calculate max for scaling +calc_max() { local max=0 - for res in "${FS_RESULTS[@]}" "${RS_RESULTS[@]}" "${RSC_RESULTS[@]}" "${CP_RESULTS[@]}" "${RCLONE_RESULTS[@]}"; do + for res in "${FS_RES[@]}" "${RSYNC_RES[@]}" "${RSYNCC_RES[@]}"; do local mbs=$(echo "$res" | cut -d'|' -f2) - local int_mbs=$(echo "$mbs" | awk '{printf "%d", $1+0}') - [ "$int_mbs" -gt "$max" ] && max=$int_mbs + local int=$(echo "$mbs" | awk '{printf "%d", $1+0}') + [ "$int" -gt "$max" ] 2>/dev/null && max=$int done echo $max } - -MAX_MBS=$(calc_max_mbs) +MAX_MBS=$(calc_max) [ "$MAX_MBS" -eq 0 ] && MAX_MBS=100 -# ── Print fastSyncAI results ──────────────────────────────────────────────── -echo " === fastSyncAI (Multi-connection, over TCP with ${SCENARIO_NAME}) ===" -printf " %-30s %-10s %-10s %s %s\n" "Configuration" "Time (ms)" "MB/s" "Throughput" "Verified" -printf " %-30s %-10s %-10s %s %s\n" "------------------------------" "----------" "----------" "-----------" "--------" +printf " %-22s %-10s %-10s %s\n" "Tool" "Time (ms)" "MB/s" "Throughput" +printf " %-22s %-10s %-10s %s\n" "----------------------" "----------" "----------" "------------" -for i in "${!FS_RESULTS[@]}"; do - IFS='|' read -r ms mbs verified <<< "${FS_RESULTS[$i]}" +for i in "${!FS_RES[@]}"; do + IFS='|' read -r ms mbs ok <<< "${FS_RES[$i]}" bar=$(draw_bar "$mbs" "$MAX_MBS") - verified_str=$([ "$verified" -eq 1 ] && echo "✓" || echo "✗") - printf " %-30s %-10s %-10s %s %s\n" "${FS_LABELS[$i]}" "$ms" "$mbs" "$bar" "$verified_str" + v=$([ "$ok" -eq 1 ] && echo "✓" || echo "✗") + printf " %-22s %-10s %-10s %s %s\n" "${FS_LABELS[$i]}" "$ms" "$mbs" "$bar" "$v" done -# ── Print other tool results (TCP-based, with network simulation) ──────────── -echo "" -echo " === Other Network Tools (over TCP with ${SCENARIO_NAME}) ===" -printf " %-30s %-10s %-10s %s %s\n" "Tool" "Time (ms)" "MB/s" "Throughput" "Verified" -printf " %-30s %-10s %-10s %s %s\n" "------------------------------" "----------" "----------" "-----------" "--------" - -# rsync -IFS='|' read -r ms mbs verified <<< "${RS_RESULTS[0]}" +IFS='|' read -r ms mbs ok <<< "${RSYNC_RES[0]}" bar=$(draw_bar "$mbs" "$MAX_MBS") -verified_str=$([ "$verified" -eq 1 ] && echo "✓" || echo "✗") -printf " %-30s %-10s %-10s %s %s\n" "rsync (TCP)" "$ms" "$mbs" "$bar" "$verified_str" +v=$([ "$ok" -eq 1 ] && echo "✓" || echo "✗") +printf " %-22s %-10s %-10s %s %s\n" "rsync (TCP)" "$ms" "$mbs" "$bar" "$v" -# rsync + compress -IFS='|' read -r ms mbs verified <<< "${RSC_RESULTS[0]}" +IFS='|' read -r ms mbs ok <<< "${RSYNCC_RES[0]}" bar=$(draw_bar "$mbs" "$MAX_MBS") -verified_str=$([ "$verified" -eq 1 ] && echo "✓" || echo "✗") -printf " %-30s %-10s %-10s %s %s\n" "rsync + compress (TCP)" "$ms" "$mbs" "$bar" "$verified_str" +v=$([ "$ok" -eq 1 ] && echo "✓" || echo "✗") +printf " %-22s %-10s %-10s %s %s\n" "rsync+compress (TCP)" "$ms" "$mbs" "$bar" "$v" -# rclone -if [ "${RCLONE_RESULTS[0]}" != "0|0|0" ]; then - IFS='|' read -r ms mbs verified <<< "${RCLONE_RESULTS[0]}" - bar=$(draw_bar "$mbs" "$MAX_MBS") - verified_str=$([ "$verified" -eq 1 ] && echo "✓" || echo "✗") - printf " %-30s %-10s %-10s %s %s\n" "rclone (TCP)" "$ms" "$mbs" "$bar" "$verified_str" -fi - -# cp (local baseline - separate section) -echo "" -echo " === Local Baseline (no network) ===" -IFS='|' read -r ms mbs verified <<< "${CP_RESULTS[0]}" -bar=$(draw_bar "$mbs" "$MAX_MBS") -verified_str=$([ "$verified" -eq 1 ] && echo "✓" || echo "✗") -printf " %-30s %-10s %-10s %s %s\n" "cp (local baseline)" "$ms" "$mbs" "$bar" "$verified_str" - -# ── Find best configuration and speedup analysis ──────────────────────────── +# Analysis echo "" echo -e "${BOLD}═══════════════════════════════════════════════════════════════════════════╗${RESET}" echo -e "${BOLD} ANALYSIS${RESET}" echo -e "${BOLD}═══════════════════════════════════════════════════════════════════════════╝${RESET}" echo "" -# Find best fastSyncAI config -best_fs_idx=0 -best_fs_mbs=0 -for i in "${!FS_RESULTS[@]}"; do - IFS='|' read -r ms mbs verified <<< "${FS_RESULTS[$i]}" - mbs_int=$(echo "$mbs" | awk '{printf "%d", $1+0}') - if [ "$mbs_int" -gt "$best_fs_mbs" ]; then - best_fs_mbs=$mbs_int - best_fs_idx=$i - fi +# Find best +best_idx=0 +best_mbs=0 +for i in "${!FS_RES[@]}"; do + this_mbs=$(echo "${FS_RES[$i]}" | cut -d'|' -f2 | awk '{printf "%d", $1+0}') + [ "$this_mbs" -gt "$best_mbs" ] && best_mbs=$this_mbs && best_idx=$i done +best_label="${FS_LABELS[$best_idx]}" -# Get rsync MB/s for comparison -IFS='|' read -r rs_ms rs_mbs rs_verified <<< "${RS_RESULTS[0]}" +IFS='|' read -r rs_ms rs_mbs rs_ok <<< "${RSYNC_RES[0]}" +rs_mbs_int=$(echo "$rs_mbs" | awk '{printf "%d", $1+0}') -ok "Best fastSyncAI config: ${FS_LABELS[$best_fs_idx]} (${best_fs_mbs} MB/s)" +ok "Best fastSyncAI: $best_label (${best_mbs} MB/s)" -# Speedup vs rsync (both over same network conditions) -echo "" -echo "Speedup vs rsync (both over ${SCENARIO_NAME}):" -if [ "$(awk "BEGIN {print ($rs_mbs > 0) ? 1 : 0}")" = "1" ]; then - best_fs_result="${FS_RESULTS[$best_fs_idx]}" - IFS='|' read -r fs_ms fs_mbs fs_verified <<< "$best_fs_result" - speedup=$(awk "BEGIN {printf \"%.2f\", $fs_mbs / ($rs_mbs == 0 ? 0.01 : $rs_mbs)}") - - if [ "$(awk "BEGIN {print ($fs_mbs > $rs_mbs) ? 1 : 0}")" = "1" ]; then - echo -e " ${GREEN}fastSyncAI (best) is ${speedup}x faster than rsync${RESET}" - elif [ "$(awk "BEGIN {print ($rs_mbs > $fs_mbs) ? 1 : 0}")" = "1" ]; then - speedup_inv=$(awk "BEGIN {printf \"%.2f\", $rs_mbs / ($fs_mbs == 0 ? 0.01 : $fs_mbs)}") - echo -e " ${GREEN}rsync is ${speedup_inv}x faster than fastSyncAI (best)${RESET}" +if [ "$rs_mbs_int" -gt 0 ]; then + echo "" + echo "Speedup vs rsync (both over ${SCENARIO_NAME}):" + speedup=$(awk "BEGIN {printf \"%.2f\", $best_mbs / ($rs_mbs_int == 0 ? 0.01 : $rs_mbs_int)}") + if [ "$best_mbs" -gt "$rs_mbs_int" ]; then + echo -e " ${GREEN}fastSyncAI is ${speedup}x FASTER than rsync${RESET}" + elif [ "$rs_mbs_int" -gt "$best_mbs" ]; then + speedup=$(awk "BEGIN {printf \"%.2f\", $rs_mbs_int / ($best_mbs == 0 ? 0.01 : $best_mbs)}") + echo -e " ${GREEN}rsync is ${speedup}x faster than fastSyncAI${RESET}" else echo -e " Both performed similarly" fi fi -# Compare to local cp to show network overhead echo "" -echo "Network overhead (vs local cp):" -IFS='|' read -r cp_ms cp_mbs cp_verified <<< "${CP_RESULTS[0]}" -if [ "$(awk "BEGIN {print ($cp_mbs > 0) ? 1 : 0}")" = "1" ]; then - overhead_pct=$(awk "BEGIN {printf \"%.1f\", (1 - $best_fs_mbs / $cp_mbs) * 100}") - ratio=$(awk "BEGIN {printf \"%.2f\", $cp_mbs / ($best_fs_mbs == 0 ? 0.01 : $best_fs_mbs)}") - echo -e " cp is ${ratio}x faster than best fastSyncAI (${overhead_pct}% overhead from network simulation)" -fi - -echo "" -ok "Network benchmark complete!" +ok "Benchmark complete!"