@@ -5,7 +5,15 @@ set -euo pipefail
55#
66# Usage: SPICEIO_SMB_USER=user SPICEIO_SMB_PASS=pass ./scripts/bench-live.sh
77#
8- # Runs write and read throughput tests at various file sizes.
8+ # Runs write and read throughput tests at various file sizes, plus
9+ # concurrent multi-stream tests intended to saturate a 10G link.
10+ #
11+ # Environment knobs:
12+ # BENCH_CONCURRENCY parallel streams in the concurrent tests (default 8)
13+ # BENCH_MOUNT_BASELINE 1 to also benchmark a raw mount_smbfs mount of the
14+ # same share — gives a hard ceiling on what the link
15+ # can do, so we can see spiceio's translation overhead
16+ #
917# Requires: aws cli, dd, curl, bc, perl (Time::HiRes).
1018
1119SMB_SERVER=" ${SPICEIO_SMB_SERVER:- 192.168.3.148} "
@@ -15,6 +23,8 @@ SMB_DOMAIN="${SPICEIO_SMB_DOMAIN:-}"
1523REGION=" ${SPICEIO_REGION:- us-east-1} "
1624BUCKET=" ${SPICEIO_BUCKET:- bench} "
1725BIND=" ${SPICEIO_BIND:- 127.0.0.1: 18334} "
26+ CONCURRENCY=" ${BENCH_CONCURRENCY:- 8} "
27+ MOUNT_BASELINE=" ${BENCH_MOUNT_BASELINE:- 0} "
1828
1929: " ${SPICEIO_SMB_USER:? SPICEIO_SMB_USER is required} "
2030: " ${SPICEIO_SMB_PASS:? SPICEIO_SMB_PASS is required} "
3242
3343# ── Cleanup ─────────────────────────────────────────────────────────────
3444SPICEIO_PID=" "
45+ MOUNT_POINT=" "
3546cleanup () {
3647 echo " "
3748 echo " [bench] cleaning up..."
@@ -40,6 +51,10 @@ cleanup() {
4051 kill " $SPICEIO_PID " 2> /dev/null || true
4152 wait " $SPICEIO_PID " 2> /dev/null || true
4253 fi
54+ if [[ -n " $MOUNT_POINT " && -d " $MOUNT_POINT " ]]; then
55+ umount " $MOUNT_POINT " 2> /dev/null || true
56+ rmdir " $MOUNT_POINT " 2> /dev/null || true
57+ fi
4358 rm -f /tmp/spiceio-bench-*
4459}
4560trap cleanup EXIT
@@ -122,6 +137,114 @@ bench_multi_write() {
122137 rm -f " $file "
123138}
124139
140+ # Concurrent single-file PUT: N parallel uploads of `size_bytes`-each.
141+ # Aggregate throughput is what hits the link — this is the test that
142+ # meaningfully exercises a 10G NAS pipe.
143+ bench_concurrent_write () {
144+ local concurrency=$1 size_bytes=$2 label=$3
145+ local total=$(( concurrency * size_bytes))
146+ local file=" /tmp/spiceio-bench-cwrite-${label} "
147+ gen_file " $file " " $size_bytes "
148+
149+ local start end elapsed mbps
150+ start=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
151+ local pids=()
152+ for i in $( seq 1 " $concurrency " ) ; do
153+ $AWS s3 cp " $file " " s3://${BUCKET} /${PREFIX} /cw-${label} -${i} " --quiet 2> /dev/null &
154+ pids+=($! )
155+ done
156+ for pid in " ${pids[@]} " ; do
157+ wait " $pid "
158+ done
159+ end=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
160+ elapsed=$( echo " $end - $start " | bc -l)
161+ mbps=$( echo " $total / $elapsed / 1048576" | bc -l)
162+ printf " PUT x%-3d %-5s %6.2fs %7.1f MiB/s (%.2f Gbit/s)\n" \
163+ " $concurrency " " $label " " $elapsed " " $mbps " \
164+ " $( echo " $mbps * 8 / 1024" | bc -l) "
165+ rm -f " $file "
166+ }
167+
168+ bench_concurrent_read () {
169+ local concurrency=$1 size_bytes=$2 label=$3
170+ local total=$(( concurrency * size_bytes))
171+
172+ local start end elapsed mbps
173+ start=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
174+ local pids=()
175+ for i in $( seq 1 " $concurrency " ) ; do
176+ $AWS s3 cp " s3://${BUCKET} /${PREFIX} /cw-${label} -${i} " " /tmp/spiceio-bench-cread-${label} -${i} " \
177+ --quiet 2> /dev/null &
178+ pids+=($! )
179+ done
180+ for pid in " ${pids[@]} " ; do
181+ wait " $pid "
182+ done
183+ end=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
184+ elapsed=$( echo " $end - $start " | bc -l)
185+ mbps=$( echo " $total / $elapsed / 1048576" | bc -l)
186+ printf " GET x%-3d %-5s %6.2fs %7.1f MiB/s (%.2f Gbit/s)\n" \
187+ " $concurrency " " $label " " $elapsed " " $mbps " \
188+ " $( echo " $mbps * 8 / 1024" | bc -l) "
189+ rm -f /tmp/spiceio-bench-cread-${label} -*
190+ }
191+
192+ # Optional raw-SMB baseline via mount_smbfs. Mounts the same share locally
193+ # and runs the same dd-based write/read tests. Establishes the hard
194+ # ceiling for what the link can do, so we can attribute spiceio's
195+ # translation overhead.
196+ bench_mount_baseline () {
197+ local user=" $SPICEIO_SMB_USER "
198+ local pass=" $SPICEIO_SMB_PASS "
199+ local server=" $SMB_SERVER "
200+ local share=" $SMB_SHARE "
201+
202+ MOUNT_POINT=" /tmp/spiceio-bench-mount-$$ "
203+ mkdir -p " $MOUNT_POINT "
204+ local escaped_pass
205+ escaped_pass=$( printf ' %s' " $pass " | perl -MURI::Escape -ne ' print uri_escape($_)' )
206+ if ! mount_smbfs -N " //${user} :${escaped_pass} @${server} /${share} " " $MOUNT_POINT " 2> /dev/null; then
207+ echo " (mount_smbfs failed — skipping baseline)"
208+ rmdir " $MOUNT_POINT " 2> /dev/null
209+ MOUNT_POINT=" "
210+ return
211+ fi
212+
213+ local target=" ${MOUNT_POINT} /${PREFIX} -mount-baseline"
214+ mkdir -p " $target "
215+
216+ local label sizes labels
217+ sizes=(104857600 524288000)
218+ labels=(" 100M" " 500M" )
219+ for idx in " ${! sizes[@]} " ; do
220+ local size_bytes=${sizes[$idx]}
221+ label=${labels[$idx]}
222+ local file=" /tmp/spiceio-bench-mountin-${label} "
223+ gen_file " $file " " $size_bytes "
224+
225+ local start end elapsed mbps
226+ start=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
227+ cp " $file " " ${target} /${label} "
228+ end=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
229+ elapsed=$( echo " $end - $start " | bc -l)
230+ mbps=$( echo " $size_bytes / $elapsed / 1048576" | bc -l)
231+ printf " PUT mount %-5s %6.2fs %7.1f MiB/s\n" " $label " " $elapsed " " $mbps "
232+
233+ start=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
234+ cp " ${target} /${label} " " ${file} .out"
235+ end=$( perl -MTime::HiRes=time -e ' printf "%.6f\n", time' )
236+ elapsed=$( echo " $end - $start " | bc -l)
237+ mbps=$( echo " $size_bytes / $elapsed / 1048576" | bc -l)
238+ printf " GET mount %-5s %6.2fs %7.1f MiB/s\n" " $label " " $elapsed " " $mbps "
239+ rm -f " $file " " ${file} .out"
240+ done
241+
242+ rm -rf " $target " 2> /dev/null
243+ umount " $MOUNT_POINT " 2> /dev/null
244+ rmdir " $MOUNT_POINT " 2> /dev/null
245+ MOUNT_POINT=" "
246+ }
247+
125248# ── Run benchmarks ──────────────────────────────────────────────────────
126249echo " "
127250echo " ═══════════════════════════════════════════════════════════════"
@@ -156,11 +279,24 @@ bench_multi_write 100 1048576 "1M"
156279bench_multi_write 20 10485760 " 10M"
157280bench_multi_write 10 52428800 " 50M"
158281
159- # Total: 1685 (write) + 1685 (read) + 800 (multi-write) = 4170 MiB transferred
282+ # Concurrent single-stream tests. Single-stream uploads top out at one TCP
283+ # connection's worth of pipe; aggregate concurrent uploads is the test
284+ # that actually saturates a 10G link.
285+ echo " "
286+ echo " ── Concurrent write throughput (x${CONCURRENCY} parallel) ──"
287+ bench_concurrent_write " $CONCURRENCY " 104857600 " 100M"
288+ bench_concurrent_write " $CONCURRENCY " 524288000 " 500M"
289+
160290echo " "
161- echo " ── Aggregate ──"
162- echo " Total written: 2485 MiB (single-file + multi-file)"
163- echo " Total read: 1685 MiB"
164- echo " Total I/O: 4170 MiB"
291+ echo " ── Concurrent read throughput (x${CONCURRENCY} parallel) ──"
292+ bench_concurrent_read " $CONCURRENCY " 104857600 " 100M"
293+ bench_concurrent_read " $CONCURRENCY " 524288000 " 500M"
294+
295+ if [[ " $MOUNT_BASELINE " == " 1" ]]; then
296+ echo " "
297+ echo " ── Raw mount_smbfs baseline (link ceiling) ──"
298+ bench_mount_baseline
299+ fi
300+
165301echo " "
166302echo " ═══════════════════════════════════════════════════════════════"
0 commit comments