MSG Chain 性能优化与基准测试指南
数据来源:MSG Chain 代码库核实
主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。
目录
1. 概述
1.1 为什么性能至关重要
MSG Chain 作为下一代 AI Agent 交互基础设施,性能直接决定了用户体验、网络承载能力和生态发展空间。在高频交易、实时 Agent 通信、大规模支付结算等场景下,毫秒级的延迟差异和数百 TPS 的吞吐差距将直接影响 MSG Chain 在竞争格局中的地位。
性能优化是一项持续性工程,需要在开发周期的每个阶段都予以关注。从智能合约编写、链参数配置,到节点部署架构,每个环节都存在优化空间。
1.2 性能维度
| 维度 | 描述 | 关键指标 | MSG Chain 目标 |
|---|---|---|---|
| TPS | 每秒交易数 | tx/s | > 10,000 |
| 延迟 | 交易确认时间 | 秒/块 | < 2s |
| Gas 效率 | 每单位计算消耗 | gas/tx | < 200k |
| 存储 | 状态读写性能 | ms/op | < 5ms |
| 网络 | P2P 传播延迟 | ms/block | < 500ms |
| Indexer | 数据索引吞吐 | events/s | > 50,000 |
1.3 性能优化的权衡
性能优化往往伴随取舍:
- TPS vs 去中心化: 提高块大小和出块频率会降低运行节点的硬件门槛要求,但可能增加中心化风险
- 延迟 vs 安全性: 更快的最终确认时间意味着更少的验证轮次
- 存储效率 vs 可查性: 激进的存储优化可能牺牲链上数据的可审计性
- Gas 优化 vs 可读性: 深度优化的 WASM 字节码难以调试和维护
1.4 基准测试方法论
MSG Chain 采用以下基准测试原则:
- 可重复性: 所有基准测试在隔离环境中运行,记录硬件配置
- 真实性: 模拟主网交易负载分布,而非使用线性递增模型
- 全面性: 覆盖正常负载、峰值负载和边缘情况
- 自动化: 集成 CI/CD 管线,每次合并前自动运行回归测试
# 基本性能信息查询
msgd status --node tcp://localhost:26657
msgd query block --node tcp://localhost:26657
msgd query tx --type=hash --hash <tx-hash>
1.5 性能基准环境要求
建议使用专用基准测试机器,配置不低于:
| 组件 | 最低配置 | 推荐配置 |
|---|---|---|
| CPU | 16 核, 3.0 GHz | 32 核, 3.5 GHz+ |
| RAM | 64 GB | 128 GB DDR5 |
| 磁盘 | 1 TB NVMe SSD | 2 TB NVMe SSD (RAID 0) |
| 网络 | 1 Gbps | 10 Gbps |
| OS | Ubuntu 22.04 | Ubuntu 24.04 LTS |
2. 链级性能基准
2.1 内置基准测试工具
MSG Chain 提供了完整的链级基准测试工具集,基于 Cosmos SDK 和 CometBFT 的 benchmark 框架构建。
# 安装基准测试二进制
make build-bench
# 或使用预编译版本
wget https://github.com/msgchain/mainnet/releases/download/v1.0.0/msgd-bench-linux-amd64.tar.gz
tar -xzf msgd-bench-linux-amd64.tar.gz
sudo mv msgd-bench /usr/local/bin/msgd-bench
2.1.1 TPS 基准测试
# 基础 TPS 测试
msgd benchmark tps \
--duration 60 \
--concurrency 100 \
--tx-interval 10ms \
--gas 200000 \
--fees 250umsg
# 使用自定义交易生成器
msgd benchmark tps \
--duration 120 \
--concurrency 200 \
--batch-size 50 \
--distribute-type random \
--account-count 1000 \
--prefund 1000000000umsg
# 压力测试模式
msgd benchmark tps \
--duration 300 \
--concurrency 500 \
--ramp-up 30 \
--target-tps 15000 \
--report-interval 5
2.1.2 延迟基准测试
# 交易延迟测量
msgd benchmark latency \
--tx-count 1000 \
--broadcast-mode sync \
--timeout 60s \
--mempool-check
# 分位数延迟 (p50, p95, p99, p99.9)
msgd benchmark latency \
--tx-count 5000 \
--percentiles 50,95,99,99.9 \
--output-format json \
--output-file latency_results.json
# 端到端延迟 (从提交到最终确认)
msgd benchmark latency \
--tx-count 2000 \
--measurement full \
--block-wait-timeout 30s
2.1.3 WASM 执行基准测试
# WASM 合约执行基准
msgd benchmark wasm-exec \
--contract-counter 10 \
--executions-per-contract 100 \
--complexity medium
# 高级 WASM 基准
msgd benchmark wasm-exec \
--contracts-dir ./benchmark_contracts \
--load-wasm ./cw20_optimized.wasm \
--executions 10000 \
--track-gas \
--profile-cpu \
--output flamegraph.svg
2.2 详细 TPS 基准测试
2.2.1 单节点 TPS 基准
#!/bin/bash
# single_node_tps_bench.sh --- 单节点 TPS 基准测试
set -euo pipefail
NODE="tcp://localhost:26657"
CHAIN_ID="msgchain-1"
RESULTS_DIR="./bench_results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULTS_DIR"
echo "=== MSG Chain 单节点 TPS 基准测试 ==="
echo "链 ID: $CHAIN_ID"
echo "节点: $NODE"
echo "结果目录: $RESULTS_DIR"
echo ""
# 清理旧数据
echo "[1/6] 重置链状态..."
msgd unsafe-reset-all
rm -rf ~/.msgd/data/*
# 初始化链
echo "[2/6] 初始化链..."
msgd init benchmark-node --chain-id "$CHAIN_ID"
msgd config chain-id "$CHAIN_ID"
msgd config keyring-backend test
# 创建测试账户
echo "[3/6] 创建测试账户..."
msgd keys add bench-account-1 --keyring-backend test
msgd keys add bench-account-2 --keyring-backend test
# 准备创世文件
msgd genesis add-genesis-account bench-account-1 1000000000000umsg
msgd genesis add-genesis-account bench-account-2 1000000000000umsg
msgd genesis gentx bench-account-1 500000000000umsg --chain-id "$CHAIN_ID"
msgd genesis collect-gentxs
# 调整配置
echo "[4/6] 优化节点配置..."
sed -i 's/timeout_commit = "5s"/timeout_commit = "1s"/' ~/.msgd/config/config.toml
sed -i 's/timeout_propose = "3s"/timeout_propose = "500ms"/' ~/.msgd/config/config.toml
sed -i 's/max_tx_bytes = 1048576/max_tx_bytes = 2097152/' ~/.msgd/config/config.toml
sed -i 's/max_block_bytes = 4194304/max_block_bytes = 8388608/' ~/.msgd/config/config.toml
# 启动节点
echo "[5/6] 启动节点..."
msgd start --log_level error > "$RESULTS_DIR/node.log" 2>&1 &
NODE_PID=$!
sleep 5
# 检查节点同步状态
if ! msgd status --node "$NODE" &>/dev/null; then
echo "ERROR: 节点启动失败"
kill $NODE_PID 2>/dev/null
exit 1
fi
echo "[6/6] 执行 TPS 基准测试..."
msgd benchmark tps \
--duration 120 \
--concurrency 200 \
--node "$NODE" \
--chain-id "$CHAIN_ID" \
--output "$RESULTS_DIR/tps_results.json" \
2>&1 | tee "$RESULTS_DIR/benchmark.log"
# 收集结果
echo ""
echo "=== 基准测试完成 ==="
echo "结果已保存至: $RESULTS_DIR"
cat "$RESULTS_DIR/tps_results.json" | jq .
# 清理
kill $NODE_PID 2>/dev/null
wait $NODE_PID 2>/dev/null
echo "节点已停止。"
2.2.2 多节点 TPS 基准测试
#!/bin/bash
# multi_node_tps_bench.sh --- 多节点 TPS 基准测试
set -euo pipefail
VALIDATOR_COUNT=4
CHAIN_ID="msgchain-bench-$(date +%s)"
RESULTS_DIR="./bench_results/multi_node_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULTS_DIR"
echo "=== MSG Chain 多节点 TPS 基准测试 ==="
echo "验证节点数: $VALIDATOR_COUNT"
echo "链 ID: $CHAIN_ID"
echo ""
# 使用 cosmos-sdk 多节点部署脚本
python3 scripts/testnet.py \
--validators "$VALIDATOR_COUNT" \
--chain-id "$CHAIN_ID" \
--output-dir "$RESULTS_DIR/nodes" \
--base-port 26650 \
--keyring-backend test
# 启动所有节点
for i in $(seq 0 $((VALIDATOR_COUNT - 1))); do
NODE_DIR="$RESULTS_DIR/nodes/node$i"
msgd start \
--home "$NODE_DIR" \
--log_level error \
> "$RESULTS_DIR/node${i}_output.log" 2>&1 &
echo "节点 $i 已启动 (PID: $!)"
done
sleep 10
# 等待节点同步
echo "等待节点同步完成..."
for i in $(seq 0 $((VALIDATOR_COUNT - 1))); do
PORT=$((26657 + i * 10))
for j in $(seq 1 30); do
if msgd status --node "tcp://localhost:$PORT" &>/dev/null; then
echo " 节点 $i 已就绪 (端口 $PORT)"
break
fi
sleep 2
done
done
# 运行 TPS 基准测试
msgd benchmark tps \
--duration 180 \
--concurrency 300 \
--node "tcp://localhost:26657" \
--chain-id "$CHAIN_ID" \
--output "$RESULTS_DIR/tps_results.json" \
--distribute evenly
echo ""
echo "多节点 TPS 测试结果:"
cat "$RESULTS_DIR/tps_results.json" | jq '{
"average_tps": .average_tps,
"peak_tps": .peak_tps,
"total_tx": .total_transactions,
"block_time_avg": .avg_block_time_ms,
"validators": '$VALIDATOR_COUNT'
}'
# 清理
pkill msgd 2>/dev/null || true
2.3 CometBFT 共识优化
2.3.1 共识参数调优
# ~/.msgd/config/config.toml --- 共识性能优化配置
# CometBFT 共识引擎参数
###################################
### 共识配置 (Consensus) ###
###################################
[consensus]
# 出块超时 --- 影响 TPS 和延迟
# 降低此值可加速出块,但可能提高空块比例
timeout_propose = "500ms" # 提议阶段的超时
timeout_propose_delta = "100ms" # 提议超时的增量 (每次轮次增加)
timeout_prevote = "500ms" # 预投票阶段超时
timeout_prevote_delta = "100ms" # 预投票超时增量
timeout_precommit = "500ms" # 预提交阶段超时
timeout_precommit_delta = "100ms"
timeout_commit = "1s" # 提交后等待下一高度的时间
# 区块尺寸限制
max_tx_bytes = 2097152 # 最大交易字节数 (2 MB)
max_block_bytes = 8388608 # 最大区块字节数 (8 MB)
max_block_part_count = 128 # 区块分片数
###################################
### 内存池配置 (Mempool) ###
###################################
[mempool]
# 内存池模式
# "unordered" 提高吞吐但可能对依赖交易导致重试
# "ordered" 保证交易顺序但降低并发度
version = "unordered"
# 缓存和大小限制
size = 10000 # 内存池最大交易数量
cache_size = 50000 # LRU 缓存大小
max_txs_bytes = 1073741824 # 内存池最大字节数 (1 GB)
# 交易广播
broadcast = true # 是否广播交易给其他节点
keep_invalid_txs_in_cache = false
###################################
### P2P 网络配置 ###
###################################
[p2p]
# 连接管理
max_num_inbound_peers = 40
max_num_outbound_peers = 20
flush_throttle_timeout = "10ms"
send_rate = 通用维护记录 # 发送速率 (20 MB/s)
recv_rate = 通用维护记录 # 接收速率 (20 MB/s)
# Peer 交换协议
pex = true
persistent_peers_max_dial_period = "10s"
# 连接保活
keep_invalid_peers_in_bucket = false
2.3.2 应用层优化
// app/config.go --- 应用层性能优化
package app
import (
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
)
// PerformanceConfig 性能优化配置
type PerformanceConfig struct {
EnableWAL bool
IAVLLazyLoading bool
PruningInterval uint64
SnapshotInterval uint64
MinGasPrice sdk.DecCoin
MaxGasWantedPerBlock uint64
EnableOptimisticExec bool
GasLimitPerTx uint64
}
// DefaultPerformanceConfig 默认性能配置
func DefaultPerformanceConfig() PerformanceConfig {
return PerformanceConfig{
EnableWAL: true,
IAVLLazyLoading: true,
PruningInterval: 100,
SnapshotInterval: 5000,
MinGasPrice: sdk.NewDecCoin("umsg", sdk.NewInt(2500)),
MaxGasWantedPerBlock: 100_000_000,
EnableOptimisticExec: true,
GasLimitPerTx: 10_000_000,
}
}
// ApplyPerformanceOptimizations 应用性能优化到 BaseApp
func ApplyPerformanceOptimizations(app *baseapp.BaseApp, cfg PerformanceConfig) {
if cfg.EnableOptimisticExec {
app.SetOptimisticExecution(true)
}
app.SetMempoolRecheck(false)
app.SetMinGasPrices(sdk.NewDecCoins(cfg.MinGasPrice))
app.SetIAVLCacheSize(500_000)
app.SetIAVLDisableFastNode(false)
}
2.4 TPS 基准测试结果解读
#!/bin/bash
# analyze_tps_results.sh --- TPS 结果分析工具
set -euo pipefail
RESULTS_FILE="${1:-./tps_results.json}"
if [ ! -f "$RESULTS_FILE" ]; then
echo "Usage: $0 <results.json>"
echo "ERROR: 文件 $RESULTS_FILE 不存在"
exit 1
fi
echo "=== MSG Chain TPS 结果分析 ==="
echo "来源: $RESULTS_FILE"
echo ""
# 基本信息
echo "--- 概要 ---"
jq -r '
"总交易数: \(.total_transactions // "N/A")",
"测试时长: \(.duration_seconds // "N/A")s",
"平均 TPS: \(.average_tps // "N/A")",
"峰值 TPS: \(.peak_tps // "N/A")",
"总块数: \(.total_blocks // "N/A")",
"平均块时间: \(.avg_block_time_ms // "N/A")ms",
"平均块大小: \(.avg_block_tx_count // "N/A") tx/block"
' "$RESULTS_FILE"
echo ""
echo "--- 延迟分位数 ---"
jq -r '
"p50 (中位数): \(.latency_percentiles.p50 // "N/A")ms",
"p90: \(.latency_percentiles.p90 // "N/A")ms",
"p95: \(.latency_percentiles.p95 // "N/A")ms",
"p99: \(.latency_percentiles.p99 // "N/A")ms",
"p99.9: \(.latency_percentiles.p999 // "N/A")ms"
' "$RESULTS_FILE"
# 生成 CSV 报告
CSV_OUTPUT="${RESULTS_FILE%.json}_report.csv"
jq -r '[
"timestamp,tps,block_height,block_time_ms,tx_count,avg_gas"
] + (
.time_series // []
| map("\(.timestamp),\(.tps),\(.block_height // 0),\(.block_time_ms // 0),\(.tx_count // 0),\(.avg_gas // 0)")
) | .[]' "$RESULTS_FILE" > "$CSV_OUTPUT"
echo ""
echo "CSV 报告已生成: $CSV_OUTPUT"
# 计算相对性能评分
BASELINE_TPS=10000
ACTUAL_TPS=$(jq -r '.average_tps // 0' "$RESULTS_FILE")
SCORE=$(echo "scale=2; $ACTUAL_TPS / $BASELINE_TPS * 100" | bc 2>/dev/null || echo 0)
echo "性能评分: ${SCORE}/100"
if echo "$SCORE < 60" | bc -l | grep -q 1; then
echo ""
echo "性能评分偏低,建议检查:"
echo " - 硬件配置 (CPU/RAM/磁盘 IO)"
echo " - CometBFT 共识参数"
echo " - 网络延迟和带宽"
fi
2.5 IAVL 树性能优化
// iavl_optimization.go --- IAVL 树性能优化
package iavl
import (
"time"
"github.com/cosmos/iavl"
)
type IAVLPerformanceConfig struct {
CacheSize int
FastNodeCacheSize int
DiscardFastNode bool
SyncWrites bool
BatchSize int
CompactionInterval int
LazyLoading bool
AsyncCommit bool
PrefetchVersion int64
}
func DefaultIAVLConfig() IAVLPerformanceConfig {
return IAVLPerformanceConfig{
CacheSize: 500_000,
FastNodeCacheSize: 100_000,
DiscardFastNode: false,
SyncWrites: false,
BatchSize: 1000,
CompactionInterval: 10000,
LazyLoading: true,
AsyncCommit: true,
PrefetchVersion: 0,
}
}
type IAVLPerformanceMetrics struct {
TotalReadOps uint64
ReadDuration time.Duration
AvgReadLatency time.Duration
CacheHitRate float64
TotalWriteOps uint64
WriteDuration time.Duration
AvgWriteLatency time.Duration
CommitCount uint64
AvgCommitDuration time.Duration
TreeHeight int
}
func CollectIAVLMetrics(tree *iavl.MutableTree) IAVLPerformanceMetrics {
stats := tree.GetStorageStats()
return IAVLPerformanceMetrics{
TotalReadOps: stats.ReadOps,
ReadDuration: stats.ReadTime,
AvgReadLatency: stats.ReadTime / time.Duration(max(stats.ReadOps, 1)),
CacheHitRate: float64(stats.CacheHits) / float64(max(stats.CacheHits+stats.CacheMisses, 1)),
TotalWriteOps: stats.WriteOps,
WriteDuration: stats.WriteTime,
AvgWriteLatency: stats.WriteTime / time.Duration(max(stats.WriteOps, 1)),
CommitCount: stats.Commits,
AvgCommitDuration: stats.CommitTime / time.Duration(max(stats.Commits, 1)),
TreeHeight: tree.Height(),
}
}
2.6 状态剪枝策略
# app.toml --- 状态剪枝配置
# 裁剪策略选项:
# "default": 保留最近 362880 个状态 (约 2 周)
# "everything": 只保留当前状态 (最小存储)
# "nothing": 保留所有历史状态 (最大存储)
# "custom": 自定义裁剪
pruning = "default"
pruning-keep-recent = 362880
pruning-keep-every = 0
pruning-interval = 100
# 间隔快照 --- 用于快速同步
snapshot-interval = 5000
snapshot-keep-recent = 3
# IAVL 配置
iavl-cache-size = 500000
iavl-lazy-loading = true
# 最小 Gas 价格
minimum-gas-prices = "2500umsg"
2.7 CometBFT 性能诊断
#!/bin/bash
# cometbft_diag.sh --- CometBFT 性能诊断工具
set -euo pipefail
NODE="tcp://localhost:26657"
DIAG_DIR="./cometbft_diag_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$DIAG_DIR"
echo "=== CometBFT 性能诊断 ==="
echo "节点: $NODE"
echo ""
# 1. 检查共识状态
echo "--- 1. 共识状态 ---"
curl -s "$NODE/consensus_state" | jq '.result' > "$DIAG_DIR/consensus_state.json" 2>/dev/null
echo "共识轮次: $(jq -r '.round_state.round' "$DIAG_DIR/consensus_state.json" 2>/dev/null || echo N/A)"
echo "当前高度: $(jq -r '.round_state.height' "$DIAG_DIR/consensus_state.json" 2>/dev/null || echo N/A)"
# 2. 检查网络状态
echo ""
echo "--- 2. 网络状态 ---"
curl -s "$NODE/net_info" | jq '.result' > "$DIAG_DIR/net_info.json" 2>/dev/null
PEERS=$(jq -r '.n_peers // 0' "$DIAG_DIR/net_info.json" 2>/dev/null)
echo "连接对等节点: $PEERS"
# 3. 检查内存池大小
echo ""
echo "--- 3. 内存池状态 ---"
curl -s "$NODE/unconfirmed_txs" | jq '.result' > "$DIAG_DIR/mempool.json" 2>/dev/null
MEMPOOL_COUNT=$(jq -r '.n_txs // 0' "$DIAG_DIR/mempool.json" 2>/dev/null)
echo "未确认交易数: $MEMPOOL_COUNT"
# 4. 共识参数
echo ""
echo "--- 4. 当前共识参数 ---"
curl -s "$NODE/consensus_params" | jq '.result.consensus_params.block' > "$DIAG_DIR/consensus_params.json" 2>/dev/null
echo "最大区块字节: $(jq -r '.max_bytes' "$DIAG_DIR/consensus_params.json" 2>/dev/null)"
echo "最大 Gas: $(jq -r '.max_gas' "$DIAG_DIR/consensus_params.json" 2>/dev/null)"
# 5. 验证者状态
echo ""
echo "--- 5. 验证者信息 ---"
curl -s "$NODE/validators" | jq '.result' > "$DIAG_DIR/validators.json" 2>/dev/null
VALIDATOR_COUNT=$(jq -r '.total // 0' "$DIAG_DIR/validators.json" 2>/dev/null)
echo "活跃验证者: $VALIDATOR_COUNT"
# 6. 健康检查
echo ""
echo "--- 6. 节点健康状态 ---"
HEALTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$NODE/health")
if [ "$HEALTH_STATUS" == "200" ]; then
echo "节点健康"
else
echo "节点异常 (HTTP $HEALTH_STATUS)"
fi
echo ""
echo "诊断结果已保存至: $DIAG_DIR"
2.8 操作系统级别调优
#!/bin/bash
# optimize_os.sh --- MSG Chain 节点操作系统调优
set -euo pipefail
echo "=== MSG Chain 节点操作系统调优 ==="
echo ""
# 1. 内核参数优化
echo "[1/6] 应用内核参数优化..."
cat >> /etc/sysctl.d/99-msgchain.conf << 'EOF'
# MSG Chain 节点性能优化
# 网络调优
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_mtu_probing = 1
net.core.default_qdisc = fq
# 文件系统和 I/O
vm.dirty_ratio = 30
vm.dirty_background_ratio = 5
vm.vfs_cache_pressure = 50
vm.swappiness = 10
# 进程和内存
vm.max_map_count = 262144
kernel.numa_balancing = 0
kernel.sched_autogroup_enabled = 0
EOF
sysctl --system > /dev/null 2>&1
echo " 内核参数已更新"
# 2. 磁盘 I/O 调度器优化 (NVMe)
echo "[2/6] 优化磁盘 I/O 调度器..."
for DEV in /sys/block/nvme*; do
if [ -d "$DEV" ]; then
DEVNAME=$(basename "$DEV")
echo none > "/sys/block/$DEVNAME/queue/scheduler" 2>/dev/null
echo " $DEVNAME: 调度器已设为 none (NVMe)"
fi
done
# 3. 禁用 transparent hugepages
echo "[3/6] 禁用 transparent hugepages..."
echo never > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || true
echo never > /sys/kernel/mm/transparent_hugepage/defrag 2>/dev/null || true
echo " THP 已禁用"
# 4. 设置文件描述符限制
echo "[4/6] 设置文件描述符限制..."
cat > /etc/security/limits.d/99-msgchain.conf << 'EOF'
# MSG Chain 节点限制
* soft nofile 1048576
* hard nofile 1048576
* soft memlock unlimited
* hard memlock unlimited
EOF
echo " 文件描述符限制已设置"
# 5. CPU 调优 (关闭节能)
echo "[5/6] CPU 性能调优..."
if command -v cpupower &>/dev/null; then
cpupower frequency-set -g performance 2>/dev/null || true
echo " CPU governor 设为 performance"
fi
# 6. 关闭不必要的服务
echo "[6/6] 检查不必要服务..."
for svc in snapd cups bluetooth avahi-daemon; do
systemctl disable --now "$svc" 2>/dev/null && echo " 已禁用 $svc" || true
done
echo ""
echo "操作系统调优完成。建议重启节点以应用全部更改。"
3. 合约执行性能
3.1 WASM 执行基准测试框架
// bench_wasm.rs --- WASM 执行基准测试框架
use std::time::{Duration, Instant};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct WasmBenchResult {
pub contract_name: String,
pub total_executions: u64,
pub total_duration: Duration,
pub avg_duration: Duration,
pub min_duration: Duration,
pub max_duration: Duration,
pub total_gas: u64,
pub avg_gas: u64,
pub p50_latency: Duration,
pub p95_latency: Duration,
pub p99_latency: Duration,
pub operations: Vec<OpBenchResult>,
}
#[derive(Debug, Clone)]
pub struct OpBenchResult {
pub op_name: String,
pub count: u64,
pub avg_duration: Duration,
pub avg_gas: u64,
pub total_gas: u64,
}
pub struct WasmBenchmarker {
results: Vec<WasmBenchResult>,
}
impl WasmBenchmarker {
pub fn new() -> Self {
Self { results: Vec::new() }
}
pub fn bench_contract(
&mut self,
contract: &str,
executions: u64,
bench_fn: impl Fn(u64) -> Result<(Duration, u64), String>,
) -> WasmBenchResult {
let mut durations = Vec::with_capacity(executions as usize);
let mut gas_used = Vec::with_capacity(executions as usize);
let mut total_duration = Duration::ZERO;
let mut total_gas: u64 = 0;
for i in 0..executions {
match bench_fn(i) {
Ok((dur, gas)) => {
durations.push(dur);
gas_used.push(gas);
total_duration += dur;
total_gas += gas;
}
Err(e) => {
eprintln!("执行 {} 失败 (execution {}): {}", contract, i, e);
}
}
}
durations.sort();
let count = durations.len() as u64;
let result = WasmBenchResult {
contract_name: contract.to_string(),
total_executions: count,
total_duration,
avg_duration: total_duration / count,
min_duration: *durations.first().unwrap_or(&Duration::ZERO),
max_duration: *durations.last().unwrap_or(&Duration::ZERO),
total_gas,
avg_gas: total_gas / count,
p50_latency: durations.get((count as f64 * 0.50) as usize)
.copied().unwrap_or(Duration::ZERO),
p95_latency: durations.get((count as f64 * 0.95) as usize)
.copied().unwrap_or(Duration::ZERO),
p99_latency: durations.get((count as f64 * 0.99) as usize)
.copied().unwrap_or(Duration::ZERO),
operations: Vec::new(),
};
self.results.push(result.clone());
result
}
pub fn generate_report(&self) -> String {
let mut report = String::new();
report.push_str("# WASM 合约性能基准报告\n\n");
report.push_str("| 合约 | 执行数 | 平均耗时 | p50 | p95 | p99 | 平均 Gas |\n");
report.push_str("|------|--------|----------|-----|-----|-----|----------|\n");
for r in &self.results {
report.push_str(&format!(
"| {} | {} | {:?} | {:?} | {:?} | {:?} | {} |\n",
r.contract_name,
r.total_executions,
r.avg_duration,
r.p50_latency,
r.p95_latency,
r.p99_latency,
r.avg_gas,
));
}
report
}
}
3.2 CW20 代币合约基准测试
// bench_cw20.rs --- CW20 代币合约基准测试
#[cfg(test)]
mod cw20_benchmarks {
use cosmwasm_std::testing::{
mock_dependencies, mock_env, mock_info,
};
use cosmwasm_std::{Addr, Uint128, Coin, Empty};
use cw20::{Cw20ExecuteMsg, Cw20QueryMsg};
use std::time::Instant;
const BENCH_ITERATIONS: u64 = 100;
fn setup_cw20_contract() -> (
cw20_base::contract::InstantiateMsg,
cosmwasm_std::testing::MockApi,
cosmwasm_std::testing::MockStorage,
cosmwasm_std::testing::MockQuerier,
) {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("creator", &[Coin::new(1000000, "umsg")]);
let init_msg = cw20_base::msg::InstantiateMsg {
name: "Bench Token".to_string(),
symbol: "BENCH".to_string(),
decimals: 6,
initial_balances: vec![
cw20_base::msg::InitialBalance {
address: "alice".to_string(),
amount: Uint128::new(1_000_000_000_000),
},
cw20_base::msg::InitialBalance {
address: "bob".to_string(),
amount: Uint128::new(1_000_000_000_000),
},
],
mint: None,
marketing: None,
};
let _res = cw20_base::contract::instantiate(
deps.as_mut(),
env,
info,
init_msg,
).unwrap();
}
#[test]
fn benchmark_cw20_transfer() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("alice", &[]);
// 初始化合约
let init_msg = cw20_base::msg::InstantiateMsg {
name: "Bench Token".to_string(),
symbol: "BENCH".to_string(),
decimals: 6,
initial_balances: vec![
cw20_base::msg::InitialBalance {
address: "alice".to_string(),
amount: Uint128::new(1_000_000_000_000),
},
cw20_base::msg::InitialBalance {
address: "bob".to_string(),
amount: Uint128::new(1_000_000_000_000),
},
],
mint: None,
marketing: None,
};
cw20_base::contract::instantiate(
deps.as_mut(), env.clone(), info.clone(), init_msg,
).unwrap();
let start = Instant::now();
for _ in 0..BENCH_ITERATIONS {
let msg = Cw20ExecuteMsg::Transfer {
recipient: "bob".to_string(),
amount: Uint128::new(1000),
};
cw20_base::contract::execute(
deps.as_mut(),
env.clone(),
info.clone(),
msg,
).unwrap();
}
let duration = start.elapsed();
println!(
"CW20 Transfer: {} 次执行, 总耗时 {:?}, 平均每笔 {:?}",
BENCH_ITERATIONS,
duration,
duration / BENCH_ITERATIONS as u32
);
}
#[test]
fn benchmark_cw20_queries() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("alice", &[]);
let init_msg = cw20_base::msg::InstantiateMsg {
name: "Bench Token".to_string(),
symbol: "BENCH".to_string(),
decimals: 6,
initial_balances: vec![
cw20_base::msg::InitialBalance {
address: "alice".to_string(),
amount: Uint128::new(1_000_000_000_000),
},
],
mint: None,
marketing: None,
};
cw20_base::contract::instantiate(
deps.as_mut(), env.clone(), info, init_msg,
).unwrap();
let start = Instant::now();
for _ in 0..BENCH_ITERATIONS {
let query = Cw20QueryMsg::Balance {
address: "alice".to_string(),
};
cw20_base::contract::query(deps.as_ref(), env.clone(), query).unwrap();
}
let duration = start.elapsed();
println!(
"CW20 Query Balance: {} 次查询, 总耗时 {:?}, 平均每 {:?}",
BENCH_ITERATIONS,
duration,
duration / BENCH_ITERATIONS as u32
);
}
}
3.3 CW721 NFT 合约基准测试
// bench_cw721.rs --- CW721 NFT 合约基准测试
#[cfg(test)]
mod cw721_benchmarks {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{Addr, Coin, Empty};
use cw721::{Cw721ExecuteMsg, Cw721QueryMsg};
use cw721_base::msg::InstantiateMsg as Cw721InstantiateMsg;
use std::time::Instant;
const BENCH_ITERATIONS: u64 = 50;
const TOKEN_COUNT: u64 = 100;
fn setup_and_mint() -> (cosmwasm_std::testing::MockApi, cosmwasm_std::testing::MockStorage, cosmwasm_std::testing::MockQuerier) {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("minter", &[Coin::new(1000000, "umsg")]);
let init_msg = Cw721InstantiateMsg {
name: "Bench NFT".to_string(),
symbol: "BNFT".to_string(),
minter: "minter".to_string(),
};
cw721_base::entrypoints::instantiate(
deps.as_mut(),
env,
info,
init_msg,
).unwrap();
for i in 0..TOKEN_COUNT {
let mint_msg = Cw721ExecuteMsg::Mint {
token_id: format!("token_{}", i),
owner: "collector".to_string(),
token_uri: Some(format!("https://example.com/nft/{}.json", i)),
extension: Empty {},
};
cw721_base::entrypoints::execute(
deps.as_mut(),
mock_env(),
mock_info("minter", &[]),
mint_msg,
).unwrap();
}
}
#[test]
fn benchmark_cw721_mint() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("minter", &[Coin::new(1000000, "umsg")]);
let init_msg = Cw721InstantiateMsg {
name: "Bench NFT".to_string(),
symbol: "BNFT".to_string(),
minter: "minter".to_string(),
};
cw721_base::entrypoints::instantiate(
deps.as_mut(), env.clone(), info.clone(), init_msg,
).unwrap();
let start = Instant::now();
for i in 0..BENCH_ITERATIONS {
let msg = Cw721ExecuteMsg::Mint {
token_id: format!("bench_token_{}", i),
owner: format!("owner_{}", i),
token_uri: Some("https://example.com/nft.json".to_string()),
extension: Empty {},
};
cw721_base::entrypoints::execute(
deps.as_mut(),
env.clone(),
info.clone(),
msg,
).unwrap();
}
let duration = start.elapsed();
println!(
"CW721 Mint: {} 次铸造, 总耗时 {:?}, 平均每笔 {:?}",
BENCH_ITERATIONS,
duration,
duration / BENCH_ITERATIONS as u32
);
}
#[test]
fn benchmark_cw721_transfer() {
let mut deps = setup_and_mint();
let env = mock_env();
let info = mock_info("collector", &[]);
let start = Instant::now();
for i in 0..BENCH_ITERATIONS {
let token_idx = i % TOKEN_COUNT;
let msg = Cw721ExecuteMsg::TransferNft {
recipient: format!("new_owner_{}", i),
token_id: format!("token_{}", token_idx),
};
cw721_base::entrypoints::execute(
deps.as_mut(),
env.clone(),
info.clone(),
msg,
).unwrap();
}
let duration = start.elapsed();
println!(
"CW721 Transfer: {} 次转移, 总耗时 {:?}, 平均每笔 {:?}",
BENCH_ITERATIONS,
duration,
duration / BENCH_ITERATIONS as u32
);
}
}
3.4 AMM 合约基准测试
// bench_amm.rs --- AMM 合约基准测试
#[cfg(test)]
mod amm_benchmarks {
use cosmwasm_std::{Uint128, Decimal};
use std::time::Instant;
const BENCH_ITERATIONS: u64 = 50;
struct MockPool {
pub reserve_0: Uint128,
pub reserve_1: Uint128,
pub lp_token_supply: Uint128,
pub fee_rate: Decimal,
}
impl MockPool {
fn new(reserve_0: Uint128, reserve_1: Uint128) -> Self {
Self {
reserve_0,
reserve_1,
lp_token_supply: Uint128::new(1000000),
fee_rate: Decimal::percent(3),
}
}
fn swap_exact_in(&mut self, amount_in: Uint128, token_in_is_0: bool) -> Uint128 {
let fee = amount_in * self.fee_rate;
let amount_in_after_fee = amount_in - fee;
if token_in_is_0 {
let product = self.reserve_0 * self.reserve_1;
let new_reserve_0 = self.reserve_0 + amount_in_after_fee;
let new_reserve_1 = product / new_reserve_0;
let amount_out = self.reserve_1 - new_reserve_1;
self.reserve_0 = new_reserve_0;
self.reserve_1 = new_reserve_1;
amount_out
} else {
let product = self.reserve_0 * self.reserve_1;
let new_reserve_1 = self.reserve_1 + amount_in_after_fee;
let new_reserve_0 = product / new_reserve_1;
let amount_out = self.reserve_0 - new_reserve_0;
self.reserve_0 = new_reserve_0;
self.reserve_1 = new_reserve_1;
amount_out
}
}
fn add_liquidity(&mut self, amount_0: Uint128, amount_1: Uint128) -> Uint128 {
let liquidity = if self.lp_token_supply.is_zero() {
(amount_0 * amount_1).sqrt()
} else {
let liq_0 = amount_0 * self.lp_token_supply / self.reserve_0;
let liq_1 = amount_1 * self.lp_token_supply / self.reserve_1;
liq_0.min(liq_1)
};
self.reserve_0 += amount_0;
self.reserve_1 += amount_1;
self.lp_token_supply += liquidity;
liquidity
}
fn remove_liquidity(&mut self, lp_amount: Uint128) -> (Uint128, Uint128) {
let amount_0 = lp_amount * self.reserve_0 / self.lp_token_supply;
let amount_1 = lp_amount * self.reserve_1 / self.lp_token_supply;
self.reserve_0 -= amount_0;
self.reserve_1 -= amount_1;
self.lp_token_supply -= lp_amount;
(amount_0, amount_1)
}
}
#[test]
fn benchmark_amm_swap() {
let mut pool = MockPool::new(
Uint128::new(1_000_000_000),
Uint128::new(500_000_000),
);
let start = Instant::now();
for i in 0..BENCH_ITERATIONS {
pool.swap_exact_in(Uint128::new(1000 + i), true);
}
let duration = start.elapsed();
println!(
"AMM Swap: {} 次交换, 总耗时 {:?}, 平均每笔 {:?}",
BENCH_ITERATIONS, duration, duration / BENCH_ITERATIONS as u32
);
}
#[test]
fn benchmark_amm_liquidity() {
let mut pool = MockPool::new(
Uint128::new(1_000_000_000),
Uint128::new(500_000_000),
);
let start = Instant::now();
for i in 0..BENCH_ITERATIONS {
pool.add_liquidity(
Uint128::new(10000 + (i * 100)),
Uint128::new(5000 + (i * 50)),
);
}
let duration = start.elapsed();
println!(
"AMM AddLiquidity: {} 次添加, 总耗时 {:?}, 平均每笔 {:?}",
BENCH_ITERATIONS, duration, duration / BENCH_ITERATIONS as u32
);
}
}
3.5 Gas 消耗明细基准
// bench_gas_breakdown.rs --- Gas 消耗明细基准
#[cfg(test)]
mod gas_breakdown_benchmarks {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{Addr, Uint128, Coin, Storage};
use std::time::Instant;
use cw_storage_plus::{Item, Map};
const ITERATIONS: u64 = 1000;
#[test]
fn benchmark_storage_operations() {
let mut deps = mock_dependencies();
let store = deps.as_mut().storage;
let write_start = Instant::now();
for i in 0..ITERATIONS {
let key = format!("key_{}", i).into_bytes();
let value = format!("value_{}", i).into_bytes();
store.set(&key, &value);
}
let write_duration = write_start.elapsed();
let read_start = Instant::now();
for i in 0..ITERATIONS {
let key = format!("key_{}", i).into_bytes();
let _ = store.get(&key);
}
let read_duration = read_start.elapsed();
let delete_start = Instant::now();
for i in 0..ITERATIONS {
let key = format!("key_{}", i).into_bytes();
store.remove(&key);
}
let delete_duration = delete_start.elapsed();
println!("=== 存储操作基准 ({} 次) ===", ITERATIONS);
println!(" 写入: {:?} (平均 {:?})", write_duration, write_duration / ITERATIONS as u32);
println!(" 读取: {:?} (平均 {:?})", read_duration, read_duration / ITERATIONS as u32);
println!(" 删除: {:?} (平均 {:?})", delete_duration, delete_duration / ITERATIONS as u32);
}
#[test]
fn benchmark_map_vs_item() {
let mut deps = mock_dependencies();
let store = deps.as_mut().storage;
// Item 基准
let item = Item::<u64>::new("single_value");
let item_start = Instant::now();
for i in 0..ITERATIONS {
item.save(store, &i).unwrap();
let _: u64 = item.load(store).unwrap();
}
let item_duration = item_start.elapsed();
// Map 基准
let map = Map::<&[u8], u64>::new("map_values");
let map_start = Instant::now();
for i in 0..ITERATIONS {
let key = format!("key_{}", i);
map.save(store, key.as_bytes(), &i).unwrap();
let _: u64 = map.load(store, key.as_bytes()).unwrap();
}
let map_duration = map_start.elapsed();
println!("=== Map vs Item 基准 ({} 次) ===", ITERATIONS);
println!(" Item (读写): {:?} (平均 {:?})", item_duration, item_duration / ITERATIONS as u32);
println!(" Map (读写): {:?} (平均 {:?})", map_duration, map_duration / ITERATIONS as u32);
let speedup = if map_duration > item_duration {
format!("Item 快 {:.2}x", map_duration.as_nanos() as f64 / item_duration.as_nanos() as f64)
} else {
format!("Map 快 {:.2}x", item_duration.as_nanos() as f64 / map_duration.as_nanos() as f64)
};
println!(" 结论: {}", speedup);
}
#[test]
fn benchmark_serialization() {
use cosmwasm_std::to_binary;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
struct LargeStruct {
id: u64,
name: String,
description: String,
balances: Vec<Uint128>,
metadata: Vec<(String, String)>,
active: bool,
timestamp: u64,
}
let data = LargeStruct {
id: 12345,
name: "Benchmark Contract State".to_string(),
description: "A large struct used to benchmark serialization.".to_string(),
balances: vec![Uint128::new(1000); 100],
metadata: (0..50).map(|i| (format!("key_{}", i), format!("value_{}", i))).collect(),
active: true,
timestamp: 1712345678,
};
let ser_start = Instant::now();
for _ in 0..ITERATIONS {
let _ = to_binary(&data).unwrap();
}
let ser_duration = ser_start.elapsed();
let encoded = to_binary(&data).unwrap();
let deser_start = Instant::now();
for _ in 0..ITERATIONS {
let _: LargeStruct = cosmwasm_std::from_binary(&encoded).unwrap();
}
let deser_duration = deser_start.elapsed();
println!("=== 序列化基准 ({} 次) ===", ITERATIONS);
println!(" 序列化: {:?} (平均 {:?})", ser_duration, ser_duration / ITERATIONS as u32);
println!(" 反序列化: {:?} (平均 {:?})", deser_duration, deser_duration / ITERATIONS as u32);
println!(" 数据结构大小: {} bytes", encoded.len());
}
#[test]
fn benchmark_math_operations() {
let start = Instant::now();
let mut result = Uint128::zero();
for i in 0..ITERATIONS {
let a = Uint128::new(i * 1000);
let b = Uint128::new(i * 500);
result = a.checked_add(b).unwrap();
result = a.checked_sub(b).unwrap_or(Uint128::zero());
result = a.checked_mul(Uint128::new(2)).unwrap();
if !b.is_zero() {
result = a.checked_div(b).unwrap_or(Uint128::zero());
}
}
let duration = start.elapsed();
println!("Uint128 数学运算 ({} 次): {:?}", ITERATIONS, duration);
}
}
3.6 合约 Gas Profiler
// gas_profiler.rs --- 合约 Gas 性能分析器
use std::collections::HashMap;
use std::time::Duration;
pub struct GasProfiler {
operations: HashMap<String, Vec<u64>>,
timings: HashMap<String, Vec<Duration>>,
}
impl GasProfiler {
pub fn new() -> Self {
Self {
operations: HashMap::new(),
timings: HashMap::new(),
}
}
pub fn record(&mut self, op_name: &str, gas: u64, timing: Duration) {
self.operations
.entry(op_name.to_string())
.or_default()
.push(gas);
self.timings
.entry(op_name.to_string())
.or_default()
.push(timing);
}
pub fn generate_gas_report(&self) -> String {
let mut report = String::new();
report.push_str("## Contract Gas Profiling Report\n\n");
report.push_str("| Operation | Count | Total Gas | Avg Gas | Min Gas | Max Gas | Avg Time |\n");
report.push_str("|-----------|-------|-----------|---------|---------|---------|----------|\n");
let mut ops: Vec<&String> = self.operations.keys().collect();
ops.sort();
for op in ops {
if let Some(gas_list) = self.operations.get(op) {
let total_gas: u64 = gas_list.iter().sum();
let count = gas_list.len();
let avg_gas = total_gas / count as u64;
let min_gas = gas_list.iter().min().copied().unwrap_or(0);
let max_gas = gas_list.iter().max().copied().unwrap_or(0);
let avg_time = self.timings.get(op)
.and_then(|t| {
if t.is_empty() { None }
else { Some(t.iter().sum::<Duration>() / t.len() as u32) }
})
.map(|d| format!("{:?}", d))
.unwrap_or_else(|| "N/A".to_string());
report.push_str(&format!(
"| {} | {} | {} | {} | {} | {} | {} |\n",
op, count, total_gas, avg_gas, min_gas, max_gas, avg_time,
));
}
}
let all_gas: Vec<&u64> = self.operations.values().flat_map(|v| v.iter()).collect();
if !all_gas.is_empty() {
let total: u64 = all_gas.iter().copied().sum();
report.push_str(&format!("\n**Total Gas**: {}\n", total));
report.push_str(&format!("**Total Operations**: {}\n", all_gas.len()));
report.push_str(&format!("**Average Gas/Op**: {}\n", total / all_gas.len() as u64));
}
report
}
}
4. Gas 优化策略
4.1 存储优化
4.1.1 Item vs Map 选择策略
// storage_optimization.rs --- 存储优化策略
use cosmwasm_std::{Storage, StdResult, Addr};
use cw_storage_plus::{Item, Map, IndexedMap, MultiIndex};
// 优化建议:
//
// Item: 单值存储,Gas 最低
// - 使用场景: 合约配置、计数器、全局状态
// - Gas: ~500-1000 gas/op
//
// Map: 键值映射,中等 Gas
// - 使用场景: 用户余额、白名单、代币持有者
// - Gas: ~2000-4000 gas/op
//
// IndexedMap: 多索引键值映射,高 Gas
// - 使用场景: 需要多维度查询的数据
// - Gas: ~5000-10000 gas/op
// 优化前 --- 用 Map 存储单值
pub struct UnoptimizedState {
pub owner: Map<&'static [u8], Addr>,
pub paused: Map<&'static [u8], bool>,
}
// 优化后 --- 用 Item 替代 Map
pub struct OptimizedState {
pub owner: Item<Addr>,
pub paused: Item<bool>,
}
// 优化前 --- 每次读取多个字段
impl UnoptimizedState {
pub fn load_all(&self, store: &dyn Storage) -> StdResult<(Addr, bool)> {
let owner = self.owner.load(store, b"owner")?;
let paused = self.paused.load(store, b"paused")?;
Ok((owner, paused))
}
}
// 优化后 --- 合并为单个 Item
#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct CompactState {
pub owner: Addr,
pub paused: bool,
}
pub struct SuperOptimizedState {
pub state: Item<CompactState>,
}
4.1.2 Key 编码优化
// 不优化 --- 使用字符串拼接
pub fn encode_key_string(owner: &Addr, token_id: &str) -> String {
format!("balance:{}/{}", owner, token_id)
}
// 优化 --- 使用固定长度编码
pub fn encode_key_fixed(owner: &Addr, token_id: u64) -> Vec<u8> {
let mut key = Vec::with_capacity(32 + 8);
key.extend_from_slice(owner.as_bytes());
key.extend_from_slice(&token_id.to_be_bytes());
key
}
// 优化 --- 使用前缀 + 复合键
pub fn encode_key_prefixed(prefix: u8, key1: &[u8], key2: &[u8]) -> Vec<u8> {
let mut key = Vec::with_capacity(1 + key1.len() + key2.len());
key.push(prefix);
key.extend_from_slice(key1);
key.push(0); // 分隔符
key.extend_from_slice(key2);
key
}
// Key 长度选择指南
//
// Key 大小 | Gas 消耗 | 建议
// ------------|-----------|------
// 1-16 bytes | 基准 | 最佳
// 17-32 bytes | +10-20% | 良好
// 33-64 bytes | +30-50% | 可接受
// >64 bytes | +100%+ | 避免
4.2 消息批处理
// batch_optimization.rs --- 消息批处理优化
use cosmwasm_std::{Binary, CosmosMsg, WasmMsg, Uint128, Addr, DepsMut, Response, StdResult, Event};
// 不优化 --- 单笔转账 (多次交易)
pub fn unoptimized_transfers(
contract: &Addr,
recipients: &[(String, Uint128)],
) -> Vec<CosmosMsg> {
recipients.iter().map(|(recipient, amount)| {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract.to_string(),
msg: Binary::from(b"{}"),
funds: vec![],
})
}).collect()
}
// 优化 --- 批量转账 (单次交易)
pub fn optimized_batch_transfer(
contract: &Addr,
transfers: Vec<(String, Uint128)>,
) -> CosmosMsg {
let batch_msg = BatchTransferMsg {
transfers: transfers.into_iter().map(|(r, a)| Transfer {
recipient: r,
amount: a,
}).collect(),
};
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract.to_string(),
msg: Binary::from(cosmwasm_std::to_binary(&batch_msg).unwrap()),
funds: vec![],
})
}
#[derive(serde::Serialize)]
struct Transfer {
recipient: String,
amount: Uint128,
}
#[derive(serde::Serialize)]
struct BatchTransferMsg {
transfers: Vec<Transfer>,
}
// 批量 vs 单笔 Gas 对比
//
// 转账数量 | 单笔转账 Gas 总和 | 批量转账 Gas | 节省比例
// ---------|-------------------|--------------|--------
// 10 | ~500,000 | ~350,000 | 30%
// 50 | ~2,500,000 | ~1,200,000 | 52%
// 100 | ~5,000,000 | ~2,000,000 | 60%
pub fn batch_transfer_execute(
deps: DepsMut,
sender: Addr,
transfers: Vec<(Addr, Uint128)>,
) -> StdResult<Response> {
let mut total = Uint128::zero();
for (_, a) in &transfers {
total += a;
}
// 单次更新发送者余额
// 批量更新接收者余额
let events: Vec<Event> = transfers.iter().map(|(r, a)| {
Event::new("transfer")
.add_attribute("from", sender.to_string())
.add_attribute("to", r.to_string())
.add_attribute("amount", a.to_string())
}).collect();
Ok(Response::new().add_events(events))
}
4.3 查询优化
// query_optimization.rs --- 查询优化模式
use cosmwasm_std::{Order, StdResult, Storage, Uint128, Addr};
use cw_storage_plus::Map;
// 不优化 --- 遍历所有记录 (高 Gas,易触发 Out of Gas)
pub fn paginate_all_users(
store: &dyn Storage,
map: Map<&[u8], Uint128>,
) -> StdResult<Vec<(String, Uint128)>> {
let prefix = map.prefix(b"");
prefix
.range(store, None, None, Order::Ascending)
.map(|item| {
let (key, value) = item?;
Ok((String::from_utf8(key).unwrap_or_default(), value))
})
.collect()
}
// 优化 --- 分页查询 (推荐)
pub fn paginated_query(
store: &dyn Storage,
map: Map<&[u8], Uint128>,
start_after: Option<Vec<u8>>,
limit: usize,
) -> StdResult<Vec<(String, Uint128)>> {
let prefix = map.prefix(b"");
let start = start_after.as_deref();
prefix
.range(store, start, None::<&[u8]>, Order::Ascending)
.take(limit)
.map(|item| {
let (key, value) = item?;
Ok((String::from_utf8(key).unwrap_or_default(), value))
})
.collect()
}
// 优化 --- 使用游标分页
pub struct CursorPagination {
pub page_size: u32,
pub cursor: Option<Vec<u8>>,
}
pub fn cursor_paginated_query(
store: &dyn Storage,
map: Map<&[u8], Uint128>,
pagination: CursorPagination,
) -> StdResult<CursorResult> {
let prefix = map.prefix(b"");
let start = pagination.cursor.as_deref();
let limit = pagination.page_size as usize;
let mut items = Vec::new();
let mut next_cursor: Option<Vec<u8>> = None;
for (idx, item) in prefix
.range(store, start, None::<&[u8]>, Order::Ascending)
.enumerate()
{
if idx >= limit {
next_cursor = item.ok().map(|(k, _)| k);
break;
}
let (key, value) = item?;
items.push((String::from_utf8(key).unwrap_or_default(), value));
}
Ok(CursorResult {
items,
next_cursor,
has_more: next_cursor.is_some(),
})
}
#[derive(serde::Serialize)]
pub struct CursorResult {
pub items: Vec<(String, Uint128)>,
pub next_cursor: Option<Vec<u8>>,
pub has_more: bool,
}
4.4 Gas 调优检查清单
// MSG Chain 合约 Gas 调优检查清单
pub struct GasOptimizationChecklist;
impl GasOptimizationChecklist {
pub fn check(contract_name: &str) -> Vec<String> {
vec![
// 1. 存储检查
"使用 Item 而非 Map 存储单值".to_string(),
"合并频繁同时访问的字段为单一 struct".to_string(),
"Key 长度是否 < 32 bytes".to_string(),
"使用前缀分片避免 IAVL 树过深".to_string(),
"避免不必要的存储写入 (只在变更时保存)".to_string(),
// 2. 消息检查
"批量转账使用 batch transfer".to_string(),
"使用 ExecuteMsg 批处理组合多个操作".to_string(),
"最小化跨合约调用次数".to_string(),
// 3. 查询检查
"所有列表查询实现分页".to_string(),
"使用索引加速常用查询模式".to_string(),
"避免在 Execute 中进行复杂查询".to_string(),
// 4. 计算检查
"使用 Uint128 而非 String 表示金额".to_string(),
"预计算常量值 (如费率、阈值)".to_string(),
"避免在循环中进行序列化/反序列化".to_string(),
// 5. WASM 检查
"启用 WASM 优化 (-O3 编译)".to_string(),
"减少二进制文件大小 (移除调试符号)".to_string(),
"最小化导入函数数量".to_string(),
// 6. 安全边界
"对递归调用设置深度限制".to_string(),
"批量操作设置最大元素数".to_string(),
"防止 Gas 攻击向量 (循环放大)".to_string(),
]
}
}
4.5 CW20 Gas 优化案例
// CW20 Gas 优化实战案例
// === 优化前 ===
// Gas 消耗: ~120,000 gas/transfer
// === 优化后 ===
// 使用 &[u8] 而非 &Addr 减少反序列化
// Gas 消耗: ~85,000 gas/transfer (节省 30%)
// CW20 Gas 优化前后对比
//
// 操作 | 优化前 Gas | 优化后 Gas | 节省
// -----|-----------|-----------|-----
// Transfer | 120,000 | 85,000 | 30%
// BatchTransfer(10)| 600,000 | 320,000 | 47%
// Approve | 95,000 | 72,000 | 24%
// TransferFrom | 145,000 | 105,000 | 28%
// Balance查询 | 65,000 | 48,000 | 26%
4.6 Gas 费用预估
#!/bin/bash
# estimate_gas.sh --- 交易 Gas 预估工具
set -euo pipefail
NODE="tcp://localhost:26657"
CHAIN_ID="msgchain-1"
# 合约操作 Gas 对照表
declare -A GAS_TABLE
GAS_TABLE["cw20_transfer"]="85000"
GAS_TABLE["cw20_batch_transfer_10"]="320000"
GAS_TABLE["cw20_approve"]="72000"
GAS_TABLE["cw20_transfer_from"]="105000"
GAS_TABLE["cw721_mint"]="120000"
GAS_TABLE["cw721_transfer"]="95000"
GAS_TABLE["cw721_send_nft"]="110000"
GAS_TABLE["amm_swap"]="150000"
GAS_TABLE["amm_add_liquidity"]="180000"
GAS_TABLE["amm_remove_liquidity"]="200000"
GAS_TABLE["stake_delegate"]="130000"
GAS_TABLE["stake_undelegate"]="140000"
GAS_TABLE["ibc_transfer"]="200000"
GAS_TABLE["gov_deposit"]="100000"
GAS_TABLE["gov_vote"]="80000"
echo "=== MSG Chain Gas 费预估指南 ==="
echo ""
echo "| 操作 | 预估 Gas | 预估费用 (umsg) |"
echo "|------|----------|----------------|"
GAS_PRICE=2500
for op in "${!GAS_TABLE[@]}"; do
gas=${GAS_TABLE[$op]}
fee=$((gas * GAS_PRICE))
printf "| %s | %s | %s |\n" "$op" "$gas" "$fee"
done | sort
echo ""
echo "Gas 价格: ${GAS_PRICE} umsg"
echo "公式: 预估费用 = Gas_Limit x Gas_Price"
echo "建议: 设置 Gas 上限为预估值 1.5x"
5. 网络性能
5.1 P2P 层性能调优
# ~/.msgd/config/config.toml --- P2P 网络性能优化
[p2p]
# 对等节点连接数
max_num_inbound_peers = 60
max_num_outbound_peers = 30
# 连接保活
max_connections = 90
max_connection_age = "0s"
# 发送/接收速率限制 (bytes/s)
send_rate = 51200000 # 50 MB/s
recv_rate = 51200000 # 50 MB/s
# gRPC 配置
grpc_max_open_connections = 1000
grpc_max_recv_msg_size = 10485760 # 10 MB
# WebSocket
websocket_write_buffer_size = 4194304 # 4 MB
5.1.1 节点连接优化
#!/bin/bash
# p2p_optimize.sh --- P2P 连接优化
set -euo pipefail
echo "=== P2P 网络连接优化 ==="
CONFIG_FILE="${1:-~/.msgd/config/config.toml}"
if [ ! -f "$CONFIG_FILE" ]; then
echo "ERROR: 未找到配置文件 $CONFIG_FILE"
exit 1
fi
echo "配置文件: $CONFIG_FILE"
echo ""
# 1. 持久化对等节点
echo "[1/4] 配置持久化对等节点..."
read -p "输入持久化节点地址 (逗号分隔): " PERSISTENT_PEERS
if [ -n "$PERSISTENT_PEERS" ]; then
sed -i "s/persistent_peers = \".*\"/persistent_peers = \"$PERSISTENT_PEERS\"/" "$CONFIG_FILE"
echo "持久化节点已设置"
fi
# 2. 地址簿管理
echo "[2/4] 优化地址簿设置..."
sed -i "s/addr_book_strict = true/addr_book_strict = false/" "$CONFIG_FILE"
sed -i "s/max_addr_book_peers = .*/max_addr_book_peers = 1000/" "$CONFIG_FILE"
echo ""
echo "P2P 优化配置已应用"
echo "建议重启节点使配置生效"
5.2 区块传播优化
#!/bin/bash
# block_propagation_bench.sh --- 区块传播性能基准
set -euo pipefail
echo "=== 区块传播性能基准 ==="
echo ""
NODE_COUNT=${1:-10}
BLOCK_SIZES=(512 1024 2048 4096 8192)
RESULTS_DIR="./block_prop_bench_$(date +%Y%m%d)"
mkdir -p "$RESULTS_DIR"
echo "节点数量: $NODE_COUNT"
echo ""
for size_kb in "${BLOCK_SIZES[@]}"; do
echo "--- 测试区块大小: ${size_kb} KB ---"
TX_COUNT=$((size_kb * 1024 / 256))
PROP_START=$(date +%s%N)
for node_id in $(seq 1 $NODE_COUNT); do
msgd benchmark inject-txs \
--count "$((TX_COUNT / NODE_COUNT))" \
--node "tcp://localhost:$((26657 + node_id))" \
--async &
done
wait
PROP_END=$(date +%s%N)
PROP_TIME_MS=$(( (PROP_END - PROP_START) / 1000000 ))
echo "传播时间: ${PROP_TIME_MS}ms"
echo "${size_kb},${TX_COUNT},${PROP_TIME_MS}" >> "$RESULTS_DIR/prop_times.csv"
echo ""
done
echo "测试完成"
echo "结果: $RESULTS_DIR/prop_times.csv"
5.3 WebSocket 订阅性能
#!/bin/bash
# ws_bench.sh --- WebSocket 订阅性能基准
set -euo pipefail
NODE="tcp://localhost:26657"
WS_URL="ws://localhost:26657/websocket"
SUBSCRIPTIONS=("Tx" "NewBlock" "NewBlockHeader" "ValidatorSetUpdates")
CONCURRENT_CLIENTS=50
DURATION=30
echo "=== WebSocket 订阅性能基准 ==="
echo "节点: $NODE"
echo "WS URL: $WS_URL"
echo "并发客户端: $CONCURRENT_CLIENTS"
echo "测试时长: ${DURATION}s"
echo ""
# 使用 websocat 或 wscat 进行 WebSocket 测试
if command -v websocat &>/dev/null; then
WS_CMD="websocat"
elif command -v wscat &>/dev/null; then
WS_CMD="wscat"
else
echo "请安装 websocat 或 wscat"
exit 1
fi
echo "--- 1. 连接建立延迟 ---"
for ((i=0; i<5; i++)); do
START=$(date +%s%N)
timeout 3 $WS_CMD -n "$WS_URL" 2>/dev/null &
WS_PID=$!
wait $WS_PID 2>/dev/null
END=$(date +%s%N)
CONN_TIME=$(( (END - START) / 1000000 ))
echo " 连接 $((i+1)): ${CONN_TIME}ms"
done
echo ""
echo "--- 2. 消息吞吐量 ---"
for sub in "${SUBSCRIPTIONS[@]}"; do
echo " 订阅事件: $sub"
for ((c=0; c<CONCURRENT_CLIENTS; c++)); do
timeout $DURATION $WS_CMD -n "$WS_URL" > /dev/null 2>&1 &
done
wait
echo " $sub 完成"
done
echo ""
echo "WebSocket 基准测试完成"
5.4 带宽管理
#!/bin/bash
# bandwidth_control.sh --- MSG Chain 网络带宽管理
set -euo pipefail
INTERFACE="${1:-eth0}"
LIMIT="${2:-1000}" # 带宽限制单位 mbit
NODE_PORT=26656
echo "=== 节点带宽管理 ==="
echo "网络接口: $INTERFACE"
echo "带宽限制: ${LIMIT} mbit"
echo "节点端口: $NODE_PORT"
echo ""
# 检查 tc 工具
if ! command -v tc &>/dev/null; then
echo "请先安装 iproute2: sudo apt install iproute2"
exit 1
fi
# 清理已有规则
echo "[1/4] 清理已有 tc 规则..."
tc qdisc del dev "$INTERFACE" root 2>/dev/null || true
# 设置根队列
echo "[2/4] 设置根队列 (HTB)..."
tc qdisc add dev "$INTERFACE" root handle 1: htb default 30
# 设置带宽限制
echo "[3/4] 设置带宽限制..."
tc class add dev "$INTERFACE" parent 1: classid 1:1 htb rate "${LIMIT}mbit" burst 15k
# 为 P2P 流量设置优先级
echo "[4/4] 设置 P2P 流量优先级..."
tc filter add dev "$INTERFACE" protocol ip parent 1:0 prio 1 u32 \
match ip sport "$NODE_PORT" 0xffff \
match ip protocol 6 0xff \
flowid 1:1
echo ""
echo "带宽管理已配置"
tc -s qdisc show dev "$INTERFACE"
6. Indexer 性能
6.1 事件索引吞吐量
#!/bin/bash
# indexer_bench.sh --- MSG Chain Indexer 性能基准
set -euo pipefail
NODE="tcp://localhost:26657"
DB_URL="${DATABASE_URL:-postgres://msgchain:msgchain@localhost:5432/msgchain_indexer}"
BENCH_CONTRACTS=10
EVENTS_PER_BLOCK=100
BLOCK_COUNT=500
echo "=== MSG Chain Indexer 性能基准 ==="
echo "数据库: $DB_URL"
echo "合约数: $BENCH_CONTRACTS"
echo "每块事件: $EVENTS_PER_BLOCK"
echo "区块数: $BLOCK_COUNT"
echo ""
# 1. 事件摄取吞吐量
echo "--- 1. 事件摄取吞吐量 ---"
START=$(date +%s%N)
msgd indexer bench ingest \
--contracts "$BENCH_CONTRACTS" \
--events-per-block "$EVENTS_PER_BLOCK" \
--blocks "$BLOCK_COUNT" \
--db-url "$DB_URL" \
--workers 8 \
--batch-size 500 \
2>&1 | tee /tmp/indexer_ingest.log
END=$(date +%s%N)
DURATION_MS=$(( (END - START) / 1000000 ))
TOTAL_EVENTS=$((BENCH_CONTRACTS * EVENTS_PER_BLOCK * BLOCK_COUNT))
THROUGHPUT=$((TOTAL_EVENTS * 1000 / DURATION_MS))
echo ""
echo "总事件: $TOTAL_EVENTS"
echo "耗时: ${DURATION_MS}ms"
echo "吞吐量: ${THROUGHPUT} events/s"
echo ""
# 2. 查询性能
echo "--- 2. 查询性能 ---"
QUERIES=(
"SELECT COUNT(*) FROM events WHERE block_height > $((BLOCK_COUNT / 2))"
"SELECT contract_address, COUNT(*) as event_count FROM events GROUP BY contract_address ORDER BY event_count DESC"
"SELECT * FROM events WHERE event_type = 'transfer' AND block_height BETWEEN 100 AND 200 ORDER BY block_height"
)
for query in "${QUERIES[@]}"; do
Q_START=$(date +%s%N)
psql "$DB_URL" -c "$query" > /dev/null 2>&1
Q_END=$(date +%s%N)
Q_TIME_MS=$(( (Q_END - Q_START) / 1000000 ))
echo " 查询耗时: ${Q_TIME_MS}ms"
done
# 3. 批量插入性能
echo "--- 3. 批量插入性能 ---"
for BATCH_SIZE in 100 500 1000 5000; do
B_START=$(date +%s%N)
msgd indexer bench batch-insert \
--batch-size "$BATCH_SIZE" \
--total-events 50000 \
--db-url "$DB_URL" \
> /dev/null 2>&1
B_END=$(date +%s%N)
B_TIME_MS=$(( (B_END - B_START) / 1000000 ))
B_TPUT=$((50000 * 1000 / B_TIME_MS))
echo " 批量大小 $BATCH_SIZE: ${B_TIME_MS}ms, ${B_TPUT} events/s"
done
6.2 PostgreSQL 优化
-- postgresql_optimization.sql --- MSG Chain Indexer PostgreSQL 优化
-- ============================================
-- 表结构优化 - 使用分区表
-- ============================================
CREATE TABLE IF NOT EXISTS chain_events (
id BIGSERIAL,
block_height BIGINT NOT NULL,
tx_hash TEXT NOT NULL,
event_type TEXT NOT NULL,
contract_address TEXT,
event_data JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (block_height, id)
) PARTITION BY RANGE (block_height);
-- 按月创建分区
CREATE TABLE chain_events_2026_01 PARTITION OF chain_events
FOR VALUES FROM (0) TO (1000000);
CREATE TABLE chain_events_2026_02 PARTITION OF chain_events
FOR VALUES FROM (1000000) TO (2000000);
CREATE TABLE chain_events_2026_03 PARTITION OF chain_events
FOR VALUES FROM (2000000) TO (3000000);
-- ============================================
-- 索引优化
-- ============================================
CREATE INDEX idx_events_block_height ON chain_events (block_height DESC);
CREATE INDEX idx_events_event_type ON chain_events (event_type);
CREATE INDEX idx_events_contract ON chain_events (contract_address);
CREATE INDEX idx_events_type_block ON chain_events (event_type, block_height DESC);
CREATE INDEX idx_events_contract_block ON chain_events (contract_address, block_height DESC);
CREATE INDEX idx_events_data_gin ON chain_events USING GIN (event_data jsonb_path_ops);
-- 交易表
CREATE TABLE IF NOT EXISTS transactions (
hash TEXT PRIMARY KEY,
block_height BIGINT NOT NULL,
sender TEXT,
gas_wanted BIGINT,
gas_used BIGINT,
fee NUMERIC,
status TEXT,
tx_data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
) PARTITION BY RANGE (block_height);
CREATE INDEX idx_tx_block_height ON transactions (block_height DESC);
CREATE INDEX idx_tx_sender ON transactions (sender);
CREATE INDEX idx_tx_status ON transactions (status) WHERE status = 'failed';
-- ============================================
-- PostgreSQL 配置优化
-- ============================================
ALTER SYSTEM SET shared_buffers = '4GB';
ALTER SYSTEM SET effective_cache_size = '12GB';
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET maintenance_work_mem = '1GB';
ALTER SYSTEM SET wal_buffers = '64MB';
ALTER SYSTEM SET random_page_cost = 1.1;
ALTER SYSTEM SET effective_io_concurrency = 200;
ALTER SYSTEM SET max_parallel_workers_per_gather = 4;
ALTER SYSTEM SET max_parallel_workers = 8;
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.01;
ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.005;
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET max_wal_size = '4GB';
ALTER SYSTEM SET min_wal_size = '1GB';
-- 更新统计信息
ANALYZE chain_events;
ANALYZE transactions;
6.3 GraphQL 查询性能
// graphql_optimization.rs --- GraphQL 查询性能优化
use std::time::Instant;
pub struct GraphQLQueryMetrics {
pub query_name: String,
pub execution_time: std::time::Duration,
pub db_query_count: u32,
pub result_size: usize,
}
pub trait QueryOptimizer {
fn explain_query(&self, query: &str) -> String;
fn suggest_indexes(&self, slow_queries: &[GraphQLQueryMetrics]) -> Vec<String>;
}
pub struct DefaultQueryOptimizer;
impl QueryOptimizer for DefaultQueryOptimizer {
fn explain_query(&self, query: &str) -> String {
format!("EXPLAIN ANALYZE {}", query)
}
fn suggest_indexes(&self, slow_queries: &[GraphQLQueryMetrics]) -> Vec<String> {
let mut suggestions = Vec::new();
for q in slow_queries {
if q.execution_time.as_millis() > 100 {
suggestions.push(format!(
"Query '{}' slow ({}ms), consider adding composite indexes",
q.query_name,
q.execution_time.as_millis()
));
}
if q.db_query_count > 10 {
suggestions.push(format!(
"Query '{}' has {} DB queries, consider dataloader batching",
q.query_name,
q.db_query_count
));
}
}
suggestions
}
}
// GraphQL 最佳实践:
// 1. 使用 DataLoader 批处理数据库查询
// 2. 限制查询深度 (最大 5 层)
// 3. 实现查询复杂度分析
// 4. 使用持久化查询 (Persisted Queries)
// 5. 启用查询结果缓存 (Redis/Memcached)
7. AI Agent 性能基准
7.1 Agent API 响应时间
#!/bin/bash
# agent_api_bench.sh --- AI Agent API 响应时间基准
set -euo pipefail
API_BASE="${1:-http://localhost:8080}"
RESULTS_DIR="./agent_bench_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULTS_DIR"
echo "=== MSG Chain AI Agent API 响应时间基准 ==="
echo "API Base: $API_BASE"
echo ""
# 1. Agent 状态查询
echo "--- 1. Agent 状态查询延迟 ---"
for i in $(seq 1 10); do
START=$(date +%s%N)
curl -s "$API_BASE/api/v1/agent/status" > /dev/null 2>&1
END=$(date +%s%N)
TIME_MS=$(( (END - START) / 1000000 ))
echo " 请求 $i: ${TIME_MS}ms"
done
# 2. Agent 注册/注销
echo ""
echo "--- 2. Agent 注册吞吐量 ---"
REGISTER_DURATION=30
START=$(date +%s%N)
COUNT=0
while true; do
NOW=$(date +%s%N)
ELAPSED=$(( (NOW - START) / 1000000 ))
if [ "$ELAPSED" -gt "$((REGISTER_DURATION * 1000))" ]; then
break
fi
AGENT_ID="bench-agent-${COUNT}"
curl -s -X POST "$API_BASE/api/v1/agent/register" \
-H "Content-Type: application/json" \
-d "{\"id\":\"$AGENT_ID\",\"capabilities\":[\"text-generation\",\"code-analysis\"]}" \
> /dev/null 2>&1 &
COUNT=$((COUNT + 1))
done
wait
TOTAL_DURATION_MS=$(( ( $(date +%s%N) - START) / 1000000 ))
THROUGHPUT=$((COUNT * 1000 / TOTAL_DURATION_MS))
echo " 总注册数: $COUNT"
echo " 测试时长: ${TOTAL_DURATION_MS}ms"
echo " 吞吐量: ${THROUGHPUT} reg/s"
# 3. Agent 发现查询
echo ""
echo "--- 3. Agent 发现查询延迟 ---"
for cap in "text-generation" "code-analysis" "image-generation" "data-processing"; do
START=$(date +%s%N)
for i in $(seq 1 20); do
curl -s "$API_BASE/api/v1/agent/find?capability=$cap" > /dev/null 2>&1 &
done
wait
END=$(date +%s%N)
AVG_MS=$(( (END - START) / 通用维护记录 ))
echo " 按能力 '$cap' 查询: avg ${AVG_MS}ms"
done
7.2 A2A 消息延迟基准
// a2a_bench.rs --- Agent-to-Agent 消息延迟基准
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct A2ABenchResult {
pub total_messages: u64,
pub total_duration: Duration,
pub avg_latency: Duration,
pub p50_latency: Duration,
pub p95_latency: Duration,
pub p99_latency: Duration,
pub throughput: f64,
}
pub fn benchmark_a2a_messaging(
agent_pairs: u32,
messages_per_pair: u32,
payload_size: usize,
) -> A2ABenchResult {
let mut latencies = Vec::new();
let start = Instant::now();
for pair in 0..agent_pairs {
let source = format!("agent_{}_source", pair);
let target = format!("agent_{}_target", pair);
for msg_id in 0..messages_per_pair {
let msg_start = Instant::now();
// 模拟 A2A 消息发送和确认
let payload = vec![0u8; payload_size];
let msg = A2AMessage {
source: source.clone(),
target: target.clone(),
id: format!("{}_{}", pair, msg_id),
payload,
timestamp: chrono::Utc::now().timestamp_nanos(),
};
// 消息传输
let _ = send_a2a_message(&msg);
let msg_duration = msg_start.elapsed();
latencies.push(msg_duration);
}
}
let total_duration = start.elapsed();
latencies.sort();
let total_msgs = (agent_pairs * messages_per_pair) as u64;
let throughput = total_msgs as f64 / total_duration.as_secs_f64();
A2ABenchResult {
total_messages: total_msgs,
total_duration,
avg_latency: total_duration / total_msgs as u32,
p50_latency: latencies[(latencies.len() as f64 * 0.50) as usize],
p95_latency: latencies[(latencies.len() as f64 * 0.95) as usize],
p99_latency: latencies[(latencies.len() as f64 * 0.99) as usize],
throughput,
}
}
#[derive(Clone)]
pub struct A2AMessage {
pub source: String,
pub target: String,
pub id: String,
pub payload: Vec<u8>,
pub timestamp: i64,
}
pub fn send_a2a_message(msg: &A2AMessage) -> Result<(), String> {
// 模拟消息发送
std::thread::sleep(Duration::from_micros(500));
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_a2a_benchmark_small() {
let result = benchmark_a2a_messaging(10, 100, 256);
println!("A2A 消息基准 (10 agents, 100 msg each, 256 bytes):");
println!(" 总消息: {}", result.total_messages);
println!(" 平均延迟: {:?}", result.avg_latency);
println!(" p50: {:?}", result.p50_latency);
println!(" p95: {:?}", result.p95_latency);
println!(" 吞吐量: {:.0} msg/s", result.throughput);
}
}
7.3 支付会话吞吐量
#!/bin/bash
# payment_session_bench.sh --- 支付会话吞吐量基准
set -euo pipefail
API_BASE="${1:-http://localhost:8080}"
RESULTS_DIR="./payment_bench_$(date +%Y%m%d)"
mkdir -p "$RESULTS_DIR"
echo "=== 支付会话吞吐量基准 ==="
echo ""
CONCURRENCY_LEVELS=(10 50 100 200)
for concurrency in "${CONCURRENCY_LEVELS[@]}"; do
echo "--- 并发度: $concurrency ---"
START=$(date +%s%N)
SUCCESS=0
FAILED=0
for ((i=0; i<concurrency; i++)); do
(
AGENT="agent_$i"
RESP=$(curl -s -X POST "$API_BASE/api/v1/payment/session" \
-H "Content-Type: application/json" \
-d "{
\"agent_id\": \"$AGENT\",
\"user_id\": \"user_bench\",
\"amount\": \"1000umsg\",
\"session_type\": \"micro\"
}" 2>/dev/null)
if echo "$RESP" | grep -q "session_id"; then
echo "SUCCESS" >> /tmp/payment_results_$$.txt
else
echo "FAILED" >> /tmp/payment_results_$$.txt
fi
) &
done
wait
SUCCESS=$(grep -c "SUCCESS" /tmp/payment_results_$$.txt 2>/dev/null || echo 0)
FAILED=$(grep -c "FAILED" /tmp/payment_results_$$.txt 2>/dev/null || echo 0)
rm -f /tmp/payment_results_$$.txt
END=$(date +%s%N)
DURATION_MS=$(( (END - START) / 1000000 ))
TPS=$(( (SUCCESS + FAILED) * 1000 / DURATION_MS ))
echo " 成功: $SUCCESS"
echo " 失败: $FAILED"
echo " 耗时: ${DURATION_MS}ms"
echo " 吞吐量: ${TPS} sessions/s"
echo ""
done
7.4 Agent 注册表查询性能
// agent_registry_bench.rs --- Agent 注册表查询性能
use std::time::Instant;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct AgentRecord {
pub id: String,
pub capabilities: Vec<String>,
pub status: AgentStatus,
pub stake: u128,
pub reputation: f64,
pub last_active: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub enum AgentStatus {
Active,
Busy,
Inactive,
Slashed,
}
pub struct AgentRegistryBench {
agents: Vec<AgentRecord>,
}
impl AgentRegistryBench {
pub fn new(count: usize) -> Self {
let mut agents = Vec::with_capacity(count);
for i in 0..count {
agents.push(AgentRecord {
id: format!("agent_{}", i),
capabilities: vec!["text".to_string(), "code".to_string()],
status: if i % 10 == 0 { AgentStatus::Inactive }
else if i % 20 == 0 { AgentStatus::Busy }
else { AgentStatus::Active },
stake: 1000 + (i as u128 * 100),
reputation: 0.5 + (i as f64 / count as f64 * 0.5),
last_active: 1712345678 - (i as u64 * 60),
});
}
Self { agents }
}
// 按能力查询
pub fn query_by_capability(&self, capability: &str) -> Vec<&AgentRecord> {
self.agents.iter()
.filter(|a| a.capabilities.contains(&capability.to_string()))
.collect()
}
// 按质押排序查询
pub fn query_top_staked(&self, limit: usize) -> Vec<&AgentRecord> {
let mut sorted: Vec<&AgentRecord> = self.agents.iter().collect();
sorted.sort_by(|a, b| b.stake.cmp(&a.stake));
sorted.truncate(limit);
sorted
}
// 按信誉排序查询
pub fn query_highest_reputation(&self, limit: usize) -> Vec<&AgentRecord> {
let mut sorted: Vec<&AgentRecord> = self.agents.iter().collect();
sorted.sort_by(|a, b| b.reputation.partial_cmp(&a.reputation).unwrap());
sorted.truncate(limit);
sorted
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bench_agent_registry() {
let bench = AgentRegistryBench::new(100_000);
// 按能力查询
let start = Instant::now();
let results = bench.query_by_capability("text");
let duration = start.elapsed();
println!("AgentRegistry: 按能力查询 (100k agents): {:?}, 结果: {}", duration, results.len());
// 按质押排序
let start = Instant::now();
let top = bench.query_top_staked(100);
let duration = start.elapsed();
println!("AgentRegistry: TOP 100 质押排序: {:?}", duration);
// 按信誉排序
let start = Instant::now();
let top_rep = bench.query_highest_reputation(100);
let duration = start.elapsed();
println!("AgentRegistry: TOP 100 信誉排序: {:?}", duration);
}
}
8. 性能测试工具链
8.1 基准测试工具安装
#!/bin/bash
# install_bench_tools.sh --- 安装 MSG Chain 性能基准测试工具
set -euo pipefail
echo "=== 安装 MSG Chain 性能基准测试工具 ==="
echo ""
# 1. 安装 Go 基准测试工具
echo "[1/5] 安装 Go 基准测试工具..."
go install github.com/msgchain/benchmark/cmd/msgd-bench@latest
go install golang.org/x/perf/cmd/benchstat@latest
# 2. 安装 Rust/Cargo 基准测试工具
echo "[2/5] 安装 Rust 基准测试工具..."
cargo install cargo-criterion
cargo install cargo-flamegraph
cargo install cargo-llvm-lines
# 3. 安装网络基准测试工具
echo "[3/5] 安装网络基准测试工具..."
if command -v apt &>/dev/null; then
sudo apt update
sudo apt install -y \
iperf3 \
nmap \
mtr \
httpie \
websocat \
wrk \
apache2-utils
fi
# 4. 安装 WASM 基准测试工具
echo "[4/5] 安装 WASM 基准测试工具..."
cargo install wasm-pack
cargo install twiggy
cargo install wasm-gc
# 5. 安装监控和可视化工具
echo "[5/5] 安装监控工具..."
cargo install flamegraph
go install github.com/google/pprof@latest
pip3 install prometheus-client
pip3 install grafana-api
echo ""
echo "基准测试工具安装完成"
echo ""
echo "已安装工具列表:"
echo " - msgd-bench: MSG Chain 链级基准测试"
echo " - benchstat: Go 基准统计"
echo " - cargo-criterion: Rust 基准测试"
echo " - flamegraph: CPU 火焰图"
echo " - iperf3: 网络带宽测试"
echo " - websocat: WebSocket 测试"
echo " - wrk: HTTP 压力测试"
echo " - twiggy: WASM 大小分析"
echo " - pprof: Go 性能分析"
8.2 CI/CD 性能回归测试
# .github/workflows/performance.yml --- CI/CD 性能回归测试
name: Performance Regression Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
benchmark:
runs-on: [self-hosted, benchmark]
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: msgchain
POSTGRES_PASSWORD: msgchain
POSTGRES_DB: msgchain_bench
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
- name: Build benchmarks
run: |
make build-bench
cargo build --release --benches
- name: Run TPS benchmark
run: |
msgd-bench tps \
--duration 60 \
--concurrency 50 \
--output ./bench_results/tps_result.json
- name: Run contract benchmarks
run: |
cargo criterion --message-format=json > ./bench_results/contract_results.json
- name: Compare with baseline
run: |
python3 scripts/compare_benchmarks.py \
--baseline ./bench_baseline.json \
--current ./bench_results/tps_result.json \
--threshold 0.05 \
--output ./bench_results/comparison.json
- name: Check for regressions
run: |
python3 scripts/check_regression.py \
--comparison ./bench_results/comparison.json
- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: ./bench_results/
- name: Notify on regression
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "性能回归检测失败! 查看详情: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_PERF_WEBHOOK }}
8.3 性能基准版基线管理
#!/bin/bash
# manage_baselines.sh --- 性能基准基线管理
set -euo pipefail
BASELINE_DIR="./bench_baselines"
mkdir -p "$BASELINE_DIR"
case "${1:-}" in
save)
# 保存当前结果为基线
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
VERSION="${2:-$(msgd version 2>/dev/null || echo unknown)}"
FILENAME="${BASELINE_DIR}/baseline_${VERSION}_${TIMESTAMP}.json"
msgd benchmark tps \
--duration 60 \
--concurrency 50 \
--output "$FILENAME" \
--quiet
echo "基线已保存: $FILENAME"
echo "版本: $VERSION"
;;
list)
echo "=== 保存的基线 ==="
ls -lh "$BASELINE_DIR"/*.json 2>/dev/null || echo "(无基线数据)"
;;
compare)
BASELINE="${2:-}"
CURRENT="${3:-}"
if [ -z "$BASELINE" ] || [ -z "$CURRENT" ]; then
echo "Usage: $0 compare <baseline.json> <current.json>"
exit 1
fi
python3 << 'PYEOF'
import json, sys
with open(sys.argv[1]) as f:
baseline = json.load(f)
with open(sys.argv[2]) as f:
current = json.load(f)
btps = baseline.get('average_tps', 0)
ctps = current.get('average_tps', 0)
change = ((ctps - btps) / btps) * 100 if btps else 0
print("=== 性能对比 ===")
print(f" 基线 TPS: {btps:.2f}")
print(f" 当前 TPS: {ctps:.2f}")
print(f" 变化: {change:+.2f}%")
if change < -5:
print(" !! 性能回归 (超过 5%)")
sys.exit(1)
elif change > 5:
print(" !! 性能提升 (超过 5%)")
else:
print(" OK 性能稳定")
PYEOF
;;
*)
echo "Usage: $0 {save|list|compare}"
exit 1
;;
esac
8.4 性能监控仪表板
# prometheus_metrics.py --- MSG Chain Prometheus 性能指标
from prometheus_client import Counter, Histogram, Gauge, start_http_server
import time
import random
# TPS 指标
tps_counter = Counter(
'msg_chain_tps_total',
'Total transactions processed',
['chain_id', 'node_id']
)
tps_gauge = Gauge(
'msg_chain_tps_current',
'Current transactions per second',
['chain_id']
)
# 延迟指标
tx_latency = Histogram(
'msg_chain_tx_latency_seconds',
'Transaction latency distribution',
['tx_type'],
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0]
)
block_time = Histogram(
'msg_chain_block_time_seconds',
'Block time distribution',
buckets=[0.1, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0]
)
# Gas 指标
gas_gauge = Gauge(
'msg_chain_gas_used',
'Gas used per block',
['contract']
)
# 网络指标
peer_count = Gauge(
'msg_chain_peer_count',
'Number of connected peers',
['node_id']
)
mempool_size = Gauge(
'msg_chain_mempool_size',
'Number of pending transactions in mempool',
['node_id']
)
# 存储指标
storage_size = Gauge(
'msg_chain_storage_size_bytes',
'Database storage size',
['type']
)
indexer_lag = Gauge(
'msg_chain_indexer_lag',
'Indexer block lag',
['indexer_id']
)
if __name__ == '__main__':
start_http_server(8000)
print("MSG Chain 性能指标暴露在 :8000/metrics")
while True:
# 模拟指标更新
tps_gauge.labels(chain_id='msgchain-1').set(random.uniform(100, 10000))
peer_count.labels(node_id='node1').set(random.randint(10, 50))
mempool_size.labels(node_id='node1').set(random.randint(0, 1000))
time.sleep(5)
8.5 Grafana 仪表板配置
{
"title": "MSG Chain 性能监控",
"panels": [
{
"title": "TPS 实时监控",
"type": "graph",
"targets": [
{
"expr": "rate(msg_chain_tps_total[1m])",
"legendFormat": "TPS"
}
],
"yaxes": [
{"label": "Transactions/s", "format": "short"}
]
},
{
"title": "交易延迟分布",
"type": "heatmap",
"targets": [
{
"expr": "histogram_quantile(0.99, rate(msg_chain_tx_latency_seconds_bucket[5m]))",
"legendFormat": "p99"
},
{
"expr": "histogram_quantile(0.95, rate(msg_chain_tx_latency_seconds_bucket[5m]))",
"legendFormat": "p95"
}
]
},
{
"title": "Gas 消耗",
"type": "graph",
"targets": [
{
"expr": "msg_chain_gas_used",
"legendFormat": "{{contract}}"
}
]
},
{
"title": "节点健康",
"type": "stat",
"targets": [
{
"expr": "msg_chain_peer_count",
"legendFormat": "Peers: {{node_id}}"
}
]
}
]
}
附录
A. 性能调优速查表
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| TPS 低 | 共识参数保守 | 缩短 timeout_commit, timeout_propose |
| 延迟高 | 块过大 | 降低 max_block_bytes |
| 高 Gas 消耗 | 存储模式不当 | Item 替代 Map, 批量操作 |
| 节点同步慢 | 带宽限制 | 提高 send_rate/recv_rate |
| Indexer 慢 | 缺少索引 | 添加复合索引, 分区表 |
| WS 断开 | 缓冲区不足 | 提高 websocket_write_buffer_size |
B. 推荐阅读
- Cosmos SDK 性能调优文档: https://docs.cosmos.network/main/architecture
- CometBFT 共识参数: https://docs.cometbft.com/v1.0/configuration
- CosmWasm Gas 优化: https://docs.cosmwasm.com/docs/smart-contracts/gas
- PostgreSQL 性能调优: https://www.postgresql.org/docs/current/performance-tips.html
C. 性能基准报告示例
=== MSG Chain 性能基准报告 ===
日期: 2026-07-07 14:30:00 UTC
链版本: v1.2.3
TPS 测试:
- 平均 TPS: 8,547
- 峰值 TPS: 12,341
- 总交易数: 512,820
- 测试时长: 60s
延迟分位数:
- p50: 245ms
- p95: 890ms
- p99: 1,234ms
Gas 消耗:
- 平均 Gas/交易: 85,000
- 最大 Gas/块: 45,000,000
区块:
- 总块数: 42
- 平均块时间: 1.43s
- 平均每块交易数: 12,210
性能评分: 85/100
本文档由 MSG Chain 核心开发团队维护
