Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ci-groups.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ excluded:
- integration_test # Requires live Dash node
- dash-fuzz # Honggfuzz binary targets, tested by fuzz.yml
- masternode-seeds-fetcher # Tooling binary; needs live dashd RPC, exercised by update-masternode-seeds.yml
- dash-spv-bench # Benchmarking binary
- git-state # Compile-time proc-macro with no tests, exercised via dash-spv
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["dash", "dash-network", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-manager", "key-wallet-ffi", "dash-spv", "dash-spv-ffi", "git-state", "dash-network-seeds", "masternode-seeds-fetcher"]
members = ["dash", "dash-network", "hashes", "internals", "fuzz", "rpc-client", "rpc-json", "rpc-integration-test", "key-wallet", "key-wallet-manager", "key-wallet-ffi", "dash-spv", "dash-spv-ffi", "git-state", "dash-network-seeds", "masternode-seeds-fetcher", "dash-spv-bench"]
resolver = "2"

[workspace.package]
Expand Down
24 changes: 24 additions & 0 deletions dash-spv-bench/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
bench-results/
bench-storage/
profiles/

*.lock

# Persistent testnet chain snapshot grown by snapshot-chain.sh (multi-GB; never committed).
chain-data/

# Transient per-peer CoW clones created by run.sh + the file that remembers their dir.
.bench-clones.*
.clonedir

# Scenario matrix outputs (bench-scenarios.sh): generated reports/logs/compose, and the
# bootstrapped yq binary. Reports are meant to be copied out, not committed.
.scenario.*.yml
results/
.bin/

# FlameGraph tooling clone (run.sh --flame), fetched on demand.
.flamegraph/

# Wallet mnemonics (one BIP39 phrase per line) — secrets, never committed.
wallets.txt
25 changes: 25 additions & 0 deletions dash-spv-bench/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "dash-spv-bench"
version = { workspace = true }
edition = "2021"
license = "MIT"
publish = false

[dependencies]
dash-spv = { path = "../dash-spv" }
dashcore = { path = "../dash", features = ["serde", "core-block-hash-use-x11", "message_verification", "bls", "quorum_validation"] }
key-wallet = { path = "../key-wallet" }
key-wallet-manager = { path = "../key-wallet-manager", features = ["parallel-filters"] }

tokio = { version = "1.0", features = ["full"] }

anyhow = "1.0"
tempfile = "3.0"

tracing = "0.1"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
indicatif = "0.18"

[[bin]]
name = "dash-spv-bench"
path = "src/main.rs"
23 changes: 23 additions & 0 deletions dash-spv-bench/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM debian:bookworm-slim

ARG DASHVERSION=23.1.4

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl iproute2 \
&& rm -rf /var/lib/apt/lists/*

# Download the Linux dashd matching the host CPU arch (amd64 -> x86_64, arm64 -> aarch64),
# keeping the same release used by contrib/setup-dashd.py.
RUN set -eux; \
arch="$(dpkg --print-architecture)"; \
case "$arch" in \
amd64) dlarch=x86_64 ;; \
arm64) dlarch=aarch64 ;; \
*) echo "unsupported arch $arch" >&2; exit 1 ;; \
esac; \
url="https://github.com/dashpay/dash/releases/download/v${DASHVERSION}/dashcore-${DASHVERSION}-${dlarch}-linux-gnu.tar.gz"; \
curl -fsSL "$url" -o /tmp/dashcore.tgz; \
tar -xzf /tmp/dashcore.tgz -C /tmp; \
cp "/tmp/dashcore-${DASHVERSION}/bin/dashd" /usr/local/bin/dashd; \
rm -rf /tmp/dashcore*; \
dashd --version | head -1
88 changes: 88 additions & 0 deletions dash-spv-bench/bench-scenarios.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash
#
# dash-spv-bench scenario matrix (LOCAL mode only).
#
# ./bench-scenarios.sh [-d scenarios]
#
# Thin loop over scenario files: for each scenarios/*.yml it calls `run.sh scenario <file>`
# once per its `repeats:` count, captures the timings run.sh prints, and writes an organized
# Markdown report (median over repeats). All the real work — compose generation, bring-up,
# CPU pinning, teardown — lives in run.sh; this only orchestrates and aggregates.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${SCRIPT_DIR}"

SCN_DIR="${SCRIPT_DIR}/scenarios"
RUN_CMD="${RUN_CMD:-${SCRIPT_DIR}/run.sh}" # overridable for testing the loop without docker
while [ $# -gt 0 ]; do
case "$1" in
-d | --dir) SCN_DIR="$2"; shift 2 ;;
-h | --help) sed -n '3,10p' "$0"; exit 0 ;;
*) echo "unknown arg: $1" >&2; exit 1 ;;
esac
done

shopt -s nullglob
FILES=("${SCN_DIR}"/*.yml)
[ "${#FILES[@]}" -gt 0 ] || { echo "Error: no scenarios (*.yml) in ${SCN_DIR}" >&2; exit 1; }

RUN_TS="$(date +%Y%m%d-%H%M%S)"
OUT_DIR="${SCRIPT_DIR}/results/${RUN_TS}"
mkdir -p "${OUT_DIR}"
REPORT="${OUT_DIR}/report.md"
TSV="${OUT_DIR}/results.tsv"
: >"${TSV}"

metric() { awk -F':[[:space:]]*' -v k="$2" '$1==k {gsub(/[[:space:]]+$/,"",$2); print $2; exit}' "$1"; }
median() { sort -n | awk '{a[NR]=$1} END{if(NR==0){print"-";exit} print (NR%2)?a[(NR+1)/2]:int((a[NR/2]+a[NR/2+1])/2)}'; }

for f in "${FILES[@]}"; do
name="$(basename "${f}" .yml)"
repeats="$(awk '/^repeats:/{print $2; exit}' "${f}")"; repeats="${repeats:-1}"
echo "===== scenario '${name}' × ${repeats} ====="
for r in $(seq 1 "${repeats}"); do
echo "-- ${name} repeat ${r}/${repeats}"
log="${OUT_DIR}/${name}.r${r}.log"
"${RUN_CMD}" "${f}" >"${log}" 2>&1 || echo " (run.sh exited non-zero; see ${log})"

# Scenario metadata comes straight from run.sh's own echo lines, so we never re-parse the YAML.
meta="$(grep -m1 "==> scenario '" "${log}" || true)"
cpus="$(sed -n "s/.*cpus=\([^,]*\),.*/\1/p" <<<"${meta}")"
blocks="$(sed -n "s/.*blocks=\([0-9]*\).*/\1/p" <<<"${meta}")"
peers="$(sed -n "s/.*\[\(.*\)\], cpus=.*/\1/p" <<<"${meta}")"
desc="$(grep -m1 "==> description:" "${log}" | sed 's/.*==> description: //' || true)"

printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"${name}" "${cpus}" "${blocks}" "${peers}" "${r}" \
"$(metric "${log}" completed)" "$(metric "${log}" total_ms)" \
"$(metric "${log}" block_headers_ms)" "$(metric "${log}" filter_headers_ms)" \
"$(metric "${log}" filters_ms)" "$(metric "${log}" filter_hdr_lag_ms)" "${desc}" \
>>"${TSV}"
done
done

# --- report ---------------------------------------------------------------------------------
{
echo "# dash-spv-bench scenario report"
echo
echo "- **Date:** $(date -u '+%Y-%m-%d %H:%M:%SZ')"
echo "- **Host:** $(nproc) cores — $(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ //' || uname -m)"
echo "- **git:** $(git -C "${REPO_ROOT}" rev-parse --abbrev-ref HEAD 2>/dev/null)@$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null)"
echo "- **Scenarios:** \`$(basename "${SCN_DIR}")/\` (${#FILES[@]} files)"
echo
echo "Timings are the **median** over each scenario's repeats (ms)."
echo
echo "| scenario | description | cpus | blocks | peers | completed | total | block_hdrs | filter_hdrs | filters | fh_lag |"
echo "|---|---|---|---|---|---|---|---|---|---|---|"
for name in $(cut -f1 "${TSV}" | awk '!seen[$0]++'); do
row() { awk -F'\t' -v n="${name}" '$1==n{print $'"$1"'; exit}' "${TSV}"; }
med() { awk -F'\t' -v n="${name}" '$1==n && $'"$1"'!="" && $'"$1"'!="-"{print $'"$1"'}' "${TSV}" | median; }
echo "| ${name} | $(row 12) | $(row 2) | $(row 3) | $(row 4) | $(row 6) | $(med 7) | $(med 8) | $(med 9) | $(med 10) | $(med 11) |"
done
echo
echo "Raw per-repeat data: \`results.tsv\`; per-run logs alongside this report."
} >"${REPORT}"

echo "==> wrote ${REPORT}"
Loading
Loading