- Collect baseline benchmark summary across all core modules - Save key results to benchmarks/v0.4.0/summary.txt - Update .gitignore to track benchmark summaries/reports - Include performance optimization design docs and plan
1236 lines
25 KiB
Markdown
1236 lines
25 KiB
Markdown
# 性能持续优化实施计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 建立完整的性能基准测试体系,收集 baseline 数据,识别 Top 10 瓶颈,实施可量化的性能优化
|
||
|
||
**Architecture:** 数据驱动优化流程:建立基准 → 采集数据 → 分析瓶颈 → 实施优化 → 回归检测。先补齐缺失的 benchmark,再跑全量基准生成 baseline,然后用 pprof 定位瓶颈,最后逐个优化验证
|
||
|
||
**Tech Stack:** Go 1.26+, testing/benchmark, pprof, benchstat, wrk/oha/h2load
|
||
|
||
---
|
||
|
||
## 文件结构映射
|
||
|
||
```
|
||
internal/benchmark/
|
||
├── micro/ # 微基准测试
|
||
│ ├── resolver_bench_test.go # DNS 解析器基准
|
||
│ ├── stream_bench_test.go # Stream 代理基准
|
||
│ ├── cache_bench_test.go # 缓存系统基准
|
||
│ ├── lua_bench_test.go # Lua 引擎基准
|
||
│ └── variable_bench_test.go # 变量系统基准
|
||
├── integration/ # 集成基准测试
|
||
│ ├── server_bench_test.go # HTTP 服务器端到端
|
||
│ ├── proxy_bench_test.go # 反向代理端到端
|
||
│ └── static_bench_test.go # 静态文件端到端
|
||
└── system/ # 系统压测脚本
|
||
├── bench.sh # 主压测脚本
|
||
├── static.lua # wrk 静态文件压测脚本
|
||
└── proxy.lua # wrk 代理压测脚本
|
||
|
||
scripts/
|
||
└── bench-suite.sh # 一键运行全量基准
|
||
|
||
benchmarks/ # 基准结果存储
|
||
└── v0.4.0/ # 版本号目录
|
||
├── micro.txt
|
||
├── integration.txt
|
||
├── system.txt
|
||
└── pprof/
|
||
├── cpu.prof
|
||
├── heap.prof
|
||
├── allocs.prof
|
||
└── goroutine.prof
|
||
```
|
||
|
||
---
|
||
|
||
## Task 1: 建立 Benchmark 目录结构
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/micro/`
|
||
- Create: `internal/benchmark/integration/`
|
||
- Create: `internal/benchmark/system/`
|
||
- Create: `benchmarks/`
|
||
- Modify: `.gitignore`(忽略 benchmarks/ 但保留目录)
|
||
|
||
- [ ] **Step 1: 创建目录结构**
|
||
|
||
```bash
|
||
mkdir -p internal/benchmark/micro
|
||
mkdir -p internal/benchmark/integration
|
||
mkdir -p internal/benchmark/system
|
||
mkdir -p benchmarks/v0.4.0/pprof
|
||
```
|
||
|
||
- [ ] **Step 2: 添加 .gitignore 规则**
|
||
|
||
在 `.gitignore` 末尾添加:
|
||
|
||
```
|
||
# Benchmark results
|
||
benchmarks/*/
|
||
!benchmarks/.gitkeep
|
||
```
|
||
|
||
创建 `benchmarks/.gitkeep`:
|
||
|
||
```bash
|
||
touch benchmarks/.gitkeep
|
||
```
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/ benchmarks/ .gitignore
|
||
git commit -m "chore(benchmark): establish benchmark directory structure"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 2: 补充缺失的微基准 — Resolver
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/micro/resolver_bench_test.go`
|
||
|
||
- [ ] **Step 1: 编写 resolver 基准测试**
|
||
|
||
```go
|
||
package micro
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"rua.plus/lolly/internal/resolver"
|
||
)
|
||
|
||
func BenchmarkResolverLookup(b *testing.B) {
|
||
// 使用 mock resolver 避免真实网络请求
|
||
r := resolver.NewMockResolver(map[string][]string{
|
||
"example.com": {"93.184.216.34"},
|
||
})
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _ = r.Lookup("example.com")
|
||
}
|
||
}
|
||
|
||
func BenchmarkResolverLookupWithCache(b *testing.B) {
|
||
r := resolver.NewMockResolver(map[string][]string{
|
||
"example.com": {"93.184.216.34"},
|
||
})
|
||
// 预热缓存
|
||
_, _ = r.Lookup("example.com")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _ = r.Lookup("example.com")
|
||
}
|
||
}
|
||
|
||
func BenchmarkResolverCacheSet(b *testing.B) {
|
||
r := resolver.NewMockResolver(nil)
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
r.CacheSet("host"+string(rune(b.N)), []string{"1.2.3.4"}, time.Minute)
|
||
}
|
||
}
|
||
|
||
func BenchmarkResolverCacheGet(b *testing.B) {
|
||
r := resolver.NewMockResolver(nil)
|
||
r.CacheSet("example.com", []string{"1.2.3.4"}, time.Minute)
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _ = r.CacheGet("example.com")
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 运行测试验证**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/resolver_bench_test.go
|
||
```
|
||
|
||
Expected: 4 个 benchmark 全部运行,无编译错误
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/micro/resolver_bench_test.go
|
||
git commit -m "feat(benchmark): add resolver micro benchmarks"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3: 补充缺失的微基准 — Stream
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/micro/stream_bench_test.go`
|
||
|
||
- [ ] **Step 1: 编写 stream 基准测试**
|
||
|
||
```go
|
||
package micro
|
||
|
||
import (
|
||
"io"
|
||
"net"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
"rua.plus/lolly/internal/stream"
|
||
)
|
||
|
||
func BenchmarkStreamTCPForward(b *testing.B) {
|
||
// 创建后端 echo 服务器
|
||
backendLn, err := net.Listen("tcp", "127.0.0.1:0")
|
||
require.NoError(b, err)
|
||
defer backendLn.Close()
|
||
|
||
go func() {
|
||
for {
|
||
conn, err := backendLn.Accept()
|
||
if err != nil {
|
||
return
|
||
}
|
||
go func(c net.Conn) {
|
||
defer c.Close()
|
||
_, _ = io.Copy(c, c)
|
||
}(conn)
|
||
}
|
||
}()
|
||
|
||
// 创建 stream server
|
||
srv := stream.NewServer()
|
||
_ = srv.AddUpstream("test", []stream.TargetSpec{
|
||
{Addr: backendLn.Addr().String(), Weight: 1},
|
||
}, "round_robin", stream.HealthCheckSpec{})
|
||
|
||
// 设置 upstream 健康
|
||
srv.SetHealthy("test", 0, true)
|
||
|
||
_ = srv.ListenTCP("127.0.0.1:0")
|
||
_ = srv.Start()
|
||
defer srv.Stop()
|
||
|
||
proxyAddr := srv.GetListenerAddr("test")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
conn, err := net.Dial("tcp", proxyAddr)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
_, _ = conn.Write([]byte("hello"))
|
||
buf := make([]byte, 5)
|
||
_, _ = io.ReadFull(conn, buf)
|
||
conn.Close()
|
||
}
|
||
}
|
||
|
||
func BenchmarkStreamSelectTarget(b *testing.B) {
|
||
srv := stream.NewServer()
|
||
_ = srv.AddUpstream("test", []stream.TargetSpec{
|
||
{Addr: "127.0.0.1:8001", Weight: 3},
|
||
{Addr: "127.0.0.1:8002", Weight: 2},
|
||
{Addr: "127.0.0.1:8003", Weight: 1},
|
||
}, "weighted_round_robin", stream.HealthCheckSpec{})
|
||
|
||
for i := 0; i < 3; i++ {
|
||
srv.SetHealthy("test", i, true)
|
||
}
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _ = srv.SelectTarget("test", nil)
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 运行测试验证**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/stream_bench_test.go
|
||
```
|
||
|
||
Expected: 2 个 benchmark 全部运行
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/micro/stream_bench_test.go
|
||
git commit -m "feat(benchmark): add stream proxy micro benchmarks"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 4: 补充缺失的微基准 — Cache
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/micro/cache_bench_test.go`
|
||
|
||
- [ ] **Step 1: 编写 cache 基准测试**
|
||
|
||
```go
|
||
package micro
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"rua.plus/lolly/internal/cache"
|
||
)
|
||
|
||
func BenchmarkCacheGet(b *testing.B) {
|
||
c := cache.New(cache.Config{MaxEntries: 10000})
|
||
_ = c.Set("key", []byte("value"), time.Hour)
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_, _ = c.Get("key")
|
||
}
|
||
}
|
||
|
||
func BenchmarkCacheSet(b *testing.B) {
|
||
c := cache.New(cache.Config{MaxEntries: 10000})
|
||
value := []byte("value")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = c.Set("key"+string(rune(b.N)), value, time.Hour)
|
||
}
|
||
}
|
||
|
||
func BenchmarkCacheGetConcurrent(b *testing.B) {
|
||
c := cache.New(cache.Config{MaxEntries: 10000})
|
||
for i := 0; i < 1000; i++ {
|
||
_ = c.Set(string(rune(i)), []byte("value"), time.Hour)
|
||
}
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
i := 0
|
||
for pb.Next() {
|
||
_, _ = c.Get(string(rune(i % 1000)))
|
||
i++
|
||
}
|
||
})
|
||
}
|
||
|
||
func BenchmarkCacheSetConcurrent(b *testing.B) {
|
||
c := cache.New(cache.Config{MaxEntries: 10000})
|
||
value := []byte("value")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
i := 0
|
||
for pb.Next() {
|
||
_ = c.Set(string(rune(i)), value, time.Hour)
|
||
i++
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 运行测试验证**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/cache_bench_test.go
|
||
```
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/micro/cache_bench_test.go
|
||
git commit -m "feat(benchmark): add cache micro benchmarks"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 5: 补充缺失的微基准 — Lua
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/micro/lua_bench_test.go`
|
||
|
||
- [ ] **Step 1: 编写 Lua 基准测试**
|
||
|
||
```go
|
||
package micro
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"rua.plus/lolly/internal/lua"
|
||
)
|
||
|
||
func BenchmarkLuaSimpleScript(b *testing.B) {
|
||
engine := lua.NewEngine()
|
||
defer engine.Close()
|
||
|
||
script := `
|
||
local a = 1 + 2
|
||
return a
|
||
`
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = engine.ExecuteString(script)
|
||
}
|
||
}
|
||
|
||
func BenchmarkLuaNginxAPI(b *testing.B) {
|
||
engine := lua.NewEngine()
|
||
defer engine.Close()
|
||
|
||
script := `
|
||
ngx.var.request_uri = "/test"
|
||
return ngx.var.request_uri
|
||
`
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = engine.ExecuteString(script)
|
||
}
|
||
}
|
||
|
||
func BenchmarkLuaJSONEncode(b *testing.B) {
|
||
engine := lua.NewEngine()
|
||
defer engine.Close()
|
||
|
||
script := `
|
||
local json = require("cjson")
|
||
local t = {name = "test", value = 123}
|
||
return json.encode(t)
|
||
`
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = engine.ExecuteString(script)
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 运行测试验证**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/lua_bench_test.go
|
||
```
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/micro/lua_bench_test.go
|
||
git commit -m "feat(benchmark): add lua engine micro benchmarks"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 6: 创建集成基准测试 — Server
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/integration/server_bench_test.go`
|
||
|
||
- [ ] **Step 1: 编写服务器集成基准**
|
||
|
||
```go
|
||
package integration
|
||
|
||
import (
|
||
"fmt"
|
||
"testing"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
"rua.plus/lolly/internal/config"
|
||
"rua.plus/lolly/internal/server"
|
||
)
|
||
|
||
func BenchmarkServerStaticRequest(b *testing.B) {
|
||
cfg := &config.Config{
|
||
Servers: []config.ServerConfig{{
|
||
Listen: "127.0.0.1:0",
|
||
Static: []config.StaticConfig{{
|
||
Path: "/",
|
||
Root: "./testdata",
|
||
}},
|
||
}},
|
||
}
|
||
|
||
srv := server.New(cfg)
|
||
go srv.Start()
|
||
defer srv.Stop()
|
||
|
||
// 等待服务器启动
|
||
addr := srv.GetAddr()
|
||
|
||
client := &fasthttp.Client{}
|
||
req := fasthttp.AcquireRequest()
|
||
resp := fasthttp.AcquireResponse()
|
||
defer fasthttp.ReleaseRequest(req)
|
||
defer fasthttp.ReleaseResponse(resp)
|
||
|
||
req.SetRequestURI("http://" + addr + "/")
|
||
req.Header.SetMethod("GET")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = client.Do(req, resp)
|
||
}
|
||
}
|
||
|
||
func BenchmarkServerProxyRequest(b *testing.B) {
|
||
// 启动后端服务器
|
||
backend := &fasthttp.Server{
|
||
Handler: func(ctx *fasthttp.RequestCtx) {
|
||
ctx.SetBodyString("ok")
|
||
},
|
||
}
|
||
go backend.ListenAndServe("127.0.0.1:18081")
|
||
|
||
cfg := &config.Config{
|
||
Servers: []config.ServerConfig{{
|
||
Listen: "127.0.0.1:0",
|
||
Proxy: []config.ProxyConfig{{
|
||
Path: "/api",
|
||
Targets: []config.ProxyTarget{{
|
||
URL: "http://127.0.0.1:18081",
|
||
}},
|
||
}},
|
||
}},
|
||
}
|
||
|
||
srv := server.New(cfg)
|
||
go srv.Start()
|
||
defer srv.Stop()
|
||
|
||
addr := srv.GetAddr()
|
||
|
||
client := &fasthttp.Client{}
|
||
req := fasthttp.AcquireRequest()
|
||
resp := fasthttp.AcquireResponse()
|
||
defer fasthttp.ReleaseRequest(req)
|
||
defer fasthttp.ReleaseResponse(resp)
|
||
|
||
req.SetRequestURI("http://" + addr + "/api/test")
|
||
req.Header.SetMethod("GET")
|
||
|
||
b.ReportAllocs()
|
||
b.ResetTimer()
|
||
for b.Loop() {
|
||
_ = client.Do(req, resp)
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 运行测试验证**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem ./internal/benchmark/integration/server_bench_test.go
|
||
```
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/integration/server_bench_test.go
|
||
git commit -m "feat(benchmark): add server integration benchmarks"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 7: 创建系统压测脚本
|
||
|
||
**Files:**
|
||
- Create: `internal/benchmark/system/bench.sh`
|
||
- Create: `internal/benchmark/system/static.lua`
|
||
- Create: `internal/benchmark/system/proxy.lua`
|
||
|
||
- [ ] **Step 1: 编写 wrk 压测脚本 — 静态文件**
|
||
|
||
`internal/benchmark/system/static.lua`:
|
||
|
||
```lua
|
||
-- wrk static file benchmark script
|
||
wrk.method = "GET"
|
||
wrk.headers["Accept"] = "text/html"
|
||
|
||
-- 随机访问不同路径增加真实感
|
||
math.randomseed(os.time())
|
||
|
||
request = function()
|
||
local paths = {"/", "/index.html", "/about.html", "/contact.html"}
|
||
local path = paths[math.random(#paths)]
|
||
return wrk.format(nil, path)
|
||
end
|
||
|
||
response = function(status, headers, body)
|
||
if status ~= 200 then
|
||
print("Error: " .. status)
|
||
end
|
||
end
|
||
```
|
||
|
||
- [ ] **Step 2: 编写 wrk 压测脚本 — 代理**
|
||
|
||
`internal/benchmark/system/proxy.lua`:
|
||
|
||
```lua
|
||
-- wrk proxy benchmark script
|
||
wrk.method = "GET"
|
||
wrk.headers["Accept"] = "application/json"
|
||
|
||
request = function()
|
||
local paths = {"/api/users", "/api/posts", "/api/comments"}
|
||
local path = paths[math.random(#paths)]
|
||
return wrk.format(nil, path)
|
||
end
|
||
```
|
||
|
||
- [ ] **Step 3: 编写主压测脚本**
|
||
|
||
`internal/benchmark/system/bench.sh`:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
# Lolly System Benchmark Suite
|
||
# Usage: ./bench.sh [lolly_addr] [duration]
|
||
|
||
ADDR=${1:-"http://127.0.0.1:8080"}
|
||
DURATION=${2:-"30s"}
|
||
CONNECTIONS=${3:-400}
|
||
THREADS=${4:-12}
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
RESULTS_DIR="${SCRIPT_DIR}/../../../benchmarks/$(date +%Y%m%d-%H%M%S)"
|
||
mkdir -p "$RESULTS_DIR"
|
||
|
||
echo "=== Lolly System Benchmark ==="
|
||
echo "Target: $ADDR"
|
||
echo "Duration: $DURATION"
|
||
echo "Connections: $CONNECTIONS"
|
||
echo "Threads: $THREADS"
|
||
echo "Results: $RESULTS_DIR"
|
||
echo ""
|
||
|
||
# Check tools
|
||
check_tool() {
|
||
if ! command -v "$1" &> /dev/null; then
|
||
echo "Warning: $1 not found, skipping related tests"
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# 1. Static file benchmark
|
||
echo "--- Static File Benchmark ---"
|
||
if check_tool wrk; then
|
||
wrk -t$THREADS -c$CONNECTIONS -d$DURATION \
|
||
-s "$SCRIPT_DIR/static.lua" \
|
||
"$ADDR" > "$RESULTS_DIR/static.txt"
|
||
echo "Static: $(grep 'Requests/sec' "$RESULTS_DIR/static.txt" || echo 'N/A')"
|
||
fi
|
||
|
||
# 2. Proxy benchmark
|
||
echo ""
|
||
echo "--- Proxy Benchmark ---"
|
||
if check_tool wrk; then
|
||
wrk -t$THREADS -c$CONNECTIONS -d$DURATION \
|
||
-s "$SCRIPT_DIR/proxy.lua" \
|
||
"$ADDR/api" > "$RESULTS_DIR/proxy.txt"
|
||
echo "Proxy: $(grep 'Requests/sec' "$RESULTS_DIR/proxy.txt" || echo 'N/A')"
|
||
fi
|
||
|
||
# 3. HTTP/2 benchmark
|
||
echo ""
|
||
echo "--- HTTP/2 Benchmark ---"
|
||
if check_tool h2load; then
|
||
h2load -n100000 -c100 -m10 "$ADDR" > "$RESULTS_DIR/http2.txt" 2>&1 || true
|
||
echo "HTTP/2: $(grep 'finished' "$RESULTS_DIR/http2.txt" || echo 'N/A')"
|
||
fi
|
||
|
||
# 4. Latency distribution with oha
|
||
echo ""
|
||
echo "--- Latency Distribution ---"
|
||
if check_tool oha; then
|
||
oha -z $DURATION -c $CONNECTIONS "$ADDR" > "$RESULTS_DIR/latency.txt"
|
||
echo "Latency: $(grep 'Success rate' "$RESULTS_DIR/latency.txt" || echo 'N/A')"
|
||
fi
|
||
|
||
echo ""
|
||
echo "=== Results saved to $RESULTS_DIR ==="
|
||
```
|
||
|
||
- [ ] **Step 4: 添加执行权限**
|
||
|
||
```bash
|
||
chmod +x internal/benchmark/system/bench.sh
|
||
```
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/benchmark/system/
|
||
git commit -m "feat(benchmark): add system benchmark scripts"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 8: 创建一键全量基准脚本
|
||
|
||
**Files:**
|
||
- Create: `scripts/bench-suite.sh`
|
||
- Modify: `Makefile`
|
||
|
||
- [ ] **Step 1: 编写一键基准脚本**
|
||
|
||
`scripts/bench-suite.sh`:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
# Run complete benchmark suite and save results
|
||
|
||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||
RESULTS_DIR="benchmarks/$VERSION"
|
||
mkdir -p "$RESULTS_DIR/pprof"
|
||
|
||
echo "=== Lolly Benchmark Suite v$VERSION ==="
|
||
echo "Results: $RESULTS_DIR"
|
||
echo ""
|
||
|
||
# 1. Micro benchmarks
|
||
echo "--- Running Micro Benchmarks ---"
|
||
go test -bench=. -benchmem \
|
||
./internal/benchmark/micro/... \
|
||
> "$RESULTS_DIR/micro.txt" 2>&1 || true
|
||
|
||
echo "Micro benchmarks done"
|
||
|
||
# 2. Integration benchmarks
|
||
echo ""
|
||
echo "--- Running Integration Benchmarks ---"
|
||
go test -bench=. -benchmem \
|
||
./internal/benchmark/integration/... \
|
||
> "$RESULTS_DIR/integration.txt" 2>&1 || true
|
||
|
||
echo "Integration benchmarks done"
|
||
|
||
# 3. Existing package benchmarks
|
||
echo ""
|
||
echo "--- Running Package Benchmarks ---"
|
||
go test -bench=. -benchmem \
|
||
./internal/loadbalance/... \
|
||
./internal/matcher/... \
|
||
./internal/proxy/... \
|
||
./internal/middleware/... \
|
||
> "$RESULTS_DIR/packages.txt" 2>&1 || true
|
||
|
||
echo "Package benchmarks done"
|
||
|
||
# 4. Summary
|
||
echo ""
|
||
echo "=== Results Summary ==="
|
||
echo "Micro: $RESULTS_DIR/micro.txt"
|
||
echo "Integration: $RESULTS_DIR/integration.txt"
|
||
echo "Packages: $RESULTS_DIR/packages.txt"
|
||
|
||
if command -v benchstat &> /dev/null; then
|
||
echo ""
|
||
echo "--- Top Results ---"
|
||
grep -h "Benchmark" "$RESULTS_DIR"/*.txt | head -20
|
||
fi
|
||
|
||
echo ""
|
||
echo "All results saved to $RESULTS_DIR"
|
||
```
|
||
|
||
- [ ] **Step 2: 添加 Makefile 目标**
|
||
|
||
在 `Makefile` 中添加:
|
||
|
||
```makefile
|
||
.PHONY: bench bench-stat bench-suite
|
||
|
||
# Run all benchmarks
|
||
bench:
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/... ./internal/benchmark/integration/...
|
||
|
||
# Run benchmarks and show statistics
|
||
bench-stat: bench
|
||
@benchstat $(shell ls benchmarks/*/micro.txt 2>/dev/null | tail -1)
|
||
|
||
# Run complete benchmark suite
|
||
bench-suite:
|
||
@bash scripts/bench-suite.sh
|
||
|
||
# Run system benchmarks (requires running server)
|
||
bench-system:
|
||
@bash internal/benchmark/system/bench.sh
|
||
```
|
||
|
||
- [ ] **Step 3: 添加执行权限**
|
||
|
||
```bash
|
||
chmod +x scripts/bench-suite.sh
|
||
```
|
||
|
||
- [ ] **Step 4: 运行测试**
|
||
|
||
```bash
|
||
make bench-suite
|
||
```
|
||
|
||
Expected: 脚本运行成功,结果保存到 `benchmarks/dev/` 目录
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add scripts/bench-suite.sh Makefile
|
||
git commit -m "feat(benchmark): add one-click benchmark suite"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 9: 运行第一轮全量基准 → 生成 Baseline
|
||
|
||
**Files:**
|
||
- Create: `benchmarks/v0.4.0/*.txt`
|
||
|
||
- [ ] **Step 1: 运行微基准**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem \
|
||
./internal/benchmark/micro/... \
|
||
> benchmarks/v0.4.0/micro.txt
|
||
```
|
||
|
||
- [ ] **Step 2: 运行已有包的基准**
|
||
|
||
```bash
|
||
go test -bench=. -benchmem \
|
||
./internal/loadbalance/... \
|
||
./internal/matcher/... \
|
||
./internal/proxy/... \
|
||
./internal/middleware/... \
|
||
./internal/server/... \
|
||
./internal/cache/... \
|
||
./internal/stream/... \
|
||
./internal/resolver/... \
|
||
./internal/variable/... \
|
||
./internal/lua/... \
|
||
> benchmarks/v0.4.0/packages.txt
|
||
```
|
||
|
||
- [ ] **Step 3: 格式化基准结果**
|
||
|
||
```bash
|
||
# 如果安装了 benchstat
|
||
benchstat benchmarks/v0.4.0/micro.txt
|
||
benchstat benchmarks/v0.4.0/packages.txt
|
||
```
|
||
|
||
- [ ] **Step 4: Commit baseline**
|
||
|
||
```bash
|
||
git add benchmarks/v0.4.0/
|
||
git commit -m "chore(benchmark): add v0.4.0 baseline performance data"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 10: 采集 pprof 数据
|
||
|
||
**Files:**
|
||
- Create: `benchmarks/v0.4.0/pprof/*.prof`
|
||
|
||
**前置条件**: 需要启动一个配置了 pprof 的 lolly 服务器
|
||
|
||
- [ ] **Step 1: 启动带 pprof 的测试服务器**
|
||
|
||
创建临时测试配置 `benchmark-pprof.yaml`:
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":8080"
|
||
static:
|
||
- path: "/"
|
||
root: "./testdata"
|
||
proxy:
|
||
- path: "/api"
|
||
targets:
|
||
- url: "http://127.0.0.1:18081"
|
||
|
||
monitoring:
|
||
pprof:
|
||
enabled: true
|
||
path: "/debug/pprof"
|
||
allow:
|
||
- "127.0.0.1"
|
||
```
|
||
|
||
启动后端 mock 服务器(可以用 Python/Node 快速启动一个 echo 服务)
|
||
|
||
启动 lolly:
|
||
|
||
```bash
|
||
./bin/lolly -c benchmark-pprof.yaml &
|
||
LOLLY_PID=$!
|
||
```
|
||
|
||
- [ ] **Step 2: 采集 CPU profile**
|
||
|
||
```bash
|
||
curl -s "http://localhost:8080/debug/pprof/profile?seconds=30" \
|
||
> benchmarks/v0.4.0/pprof/cpu.prof
|
||
```
|
||
|
||
- [ ] **Step 3: 采集 Heap profile**
|
||
|
||
```bash
|
||
curl -s "http://localhost:8080/debug/pprof/heap" \
|
||
> benchmarks/v0.4.0/pprof/heap.prof
|
||
```
|
||
|
||
- [ ] **Step 4: 采集 Allocs profile**
|
||
|
||
```bash
|
||
curl -s "http://localhost:8080/debug/pprof/allocs" \
|
||
> benchmarks/v0.4.0/pprof/allocs.prof
|
||
```
|
||
|
||
- [ ] **Step 5: 采集 Goroutine profile**
|
||
|
||
```bash
|
||
curl -s "http://localhost:8080/debug/pprof/goroutine" \
|
||
> benchmarks/v0.4.0/pprof/goroutine.prof
|
||
```
|
||
|
||
- [ ] **Step 6: 停止测试服务器**
|
||
|
||
```bash
|
||
kill $LOLLY_PID
|
||
rm benchmark-pprof.yaml
|
||
```
|
||
|
||
- [ ] **Step 7: Commit pprof 数据**
|
||
|
||
```bash
|
||
git add benchmarks/v0.4.0/pprof/
|
||
git commit -m "chore(benchmark): add v0.4.0 pprof profiles"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 11: 分析瓶颈 → 生成性能报告
|
||
|
||
**Files:**
|
||
- Create: `benchmarks/v0.4.0/REPORT.md`
|
||
|
||
- [ ] **Step 1: 分析 CPU profile**
|
||
|
||
```bash
|
||
go tool pprof -top benchmarks/v0.4.0/pprof/cpu.prof > benchmarks/v0.4.0/cpu-top.txt
|
||
```
|
||
|
||
查看 Top 20 CPU 消耗函数:
|
||
|
||
```bash
|
||
go tool pprof -top -n 20 benchmarks/v0.4.0/pprof/cpu.prof
|
||
```
|
||
|
||
- [ ] **Step 2: 分析 Heap profile**
|
||
|
||
```bash
|
||
go tool pprof -top benchmarks/v0.4.0/pprof/heap.prof > benchmarks/v0.4.0/heap-top.txt
|
||
```
|
||
|
||
- [ ] **Step 3: 分析 Allocs profile**
|
||
|
||
```bash
|
||
go tool pprof -top benchmarks/v0.4.0/pprof/allocs.prof > benchmarks/v0.4.0/allocs-top.txt
|
||
```
|
||
|
||
- [ ] **Step 4: 汇总生成报告**
|
||
|
||
`benchmarks/v0.4.0/REPORT.md`:
|
||
|
||
```markdown
|
||
# Lolly v0.4.0 性能分析报告
|
||
|
||
> 生成日期: $(date)
|
||
|
||
## 1. 基准测试摘要
|
||
|
||
### 微基准
|
||
[粘贴 micro.txt 关键结果]
|
||
|
||
### 包基准
|
||
[粘贴 packages.txt 关键结果]
|
||
|
||
## 2. CPU 热点 Top 10
|
||
|
||
[粘贴 cpu-top.txt 结果]
|
||
|
||
## 3. 内存分配热点 Top 10
|
||
|
||
[粘贴 allocs-top.txt 结果]
|
||
|
||
## 4. 内存占用 Top 10
|
||
|
||
[粘贴 heap-top.txt 结果]
|
||
|
||
## 5. 优化建议
|
||
|
||
### P0 (高优先级)
|
||
- [ ] [根据分析结果填写]
|
||
|
||
### P1 (中优先级)
|
||
- [ ] [根据分析结果填写]
|
||
|
||
### P2 (低优先级)
|
||
- [ ] [根据分析结果填写]
|
||
```
|
||
|
||
- [ ] **Step 5: Commit 报告**
|
||
|
||
```bash
|
||
git add benchmarks/v0.4.0/REPORT.md benchmarks/v0.4.0/*-top.txt
|
||
git commit -m "docs(benchmark): add v0.4.0 performance analysis report"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 12: 实施优化(基于报告)
|
||
|
||
> **注意**: 此 Task 的内容将在 Task 11 完成后根据实际瓶颈数据制定。以下为占位模板,实际实施时需替换为具体分析结果。
|
||
|
||
### Task 12.1: 优化 [瓶颈1]
|
||
|
||
**Files:**
|
||
- Modify: `internal/[package]/[file].go:[line-range]`
|
||
|
||
- [ ] **Step 1: 编写优化前 benchmark**
|
||
|
||
```bash
|
||
# 已有 baseline,无需重复
|
||
```
|
||
|
||
- [ ] **Step 2: 实施优化**
|
||
|
||
[根据实际瓶颈实施具体优化]
|
||
|
||
- [ ] **Step 3: 验证优化效果**
|
||
|
||
```bash
|
||
go test -bench=[BenchmarkName] -benchmem ./internal/[package]/...
|
||
benchstat benchmarks/v0.4.0/old.txt benchmarks/v0.4.0/new.txt
|
||
```
|
||
|
||
Expected: 性能提升 > 5%
|
||
|
||
- [ ] **Step 4: Commit**
|
||
|
||
```bash
|
||
git add internal/[package]/
|
||
git commit -m "perf([package]): optimize [description]"
|
||
```
|
||
|
||
### Task 12.2-12.N: 重复优化流程
|
||
|
||
对每个识别的瓶颈重复上述流程。
|
||
|
||
---
|
||
|
||
## Task 13: 建立性能回归检测
|
||
|
||
**Files:**
|
||
- Create: `.github/workflows/benchmark.yml` (如果恢复 CI)
|
||
- Create: `scripts/bench-compare.sh`
|
||
- Modify: `Makefile`
|
||
|
||
- [ ] **Step 1: 创建基准对比脚本**
|
||
|
||
`scripts/bench-compare.sh`:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
# Compare current benchmark against baseline
|
||
# Usage: ./bench-compare.sh [baseline_version]
|
||
|
||
BASELINE=${1:-"v0.4.0"}
|
||
BASELINE_FILE="benchmarks/$BASELINE/packages.txt"
|
||
CURRENT_FILE="benchmarks/current.txt"
|
||
|
||
if [ ! -f "$BASELINE_FILE" ]; then
|
||
echo "Baseline not found: $BASELINE_FILE"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Comparing against baseline: $BASELINE"
|
||
|
||
# Run current benchmarks
|
||
go test -bench=. -benchmem \
|
||
./internal/loadbalance/... \
|
||
./internal/matcher/... \
|
||
./internal/proxy/... \
|
||
./internal/middleware/... \
|
||
> "$CURRENT_FILE"
|
||
|
||
# Compare
|
||
if command -v benchstat &> /dev/null; then
|
||
benchstat "$BASELINE_FILE" "$CURRENT_FILE"
|
||
else
|
||
echo "benchstat not found, install with: go install golang.org/x/perf/cmd/benchstat@latest"
|
||
exit 1
|
||
fi
|
||
```
|
||
|
||
- [ ] **Step 2: 添加 Makefile 目标**
|
||
|
||
```makefile
|
||
.PHONY: bench-compare
|
||
|
||
# Compare current performance against baseline
|
||
bench-compare:
|
||
@bash scripts/bench-compare.sh
|
||
```
|
||
|
||
- [ ] **Step 3: 添加执行权限**
|
||
|
||
```bash
|
||
chmod +x scripts/bench-compare.sh
|
||
```
|
||
|
||
- [ ] **Step 4: 测试回归检测**
|
||
|
||
```bash
|
||
make bench-compare
|
||
```
|
||
|
||
Expected: 显示当前性能与 baseline 的对比,无显著退化
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add scripts/bench-compare.sh Makefile
|
||
git commit -m "feat(benchmark): add performance regression detection"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 14: 最终验证
|
||
|
||
- [ ] **Step 1: 全量测试通过**
|
||
|
||
```bash
|
||
make test
|
||
```
|
||
|
||
Expected: 全部 PASS
|
||
|
||
- [ ] **Step 2: Race 检测通过**
|
||
|
||
```bash
|
||
go test -race ./internal/...
|
||
```
|
||
|
||
Expected: 零 race
|
||
|
||
- [ ] **Step 3: Lint 通过**
|
||
|
||
```bash
|
||
make lint
|
||
```
|
||
|
||
Expected: 零 issues
|
||
|
||
- [ ] **Step 4: 构建验证**
|
||
|
||
```bash
|
||
make build
|
||
```
|
||
|
||
Expected: 构建成功
|
||
|
||
- [ ] **Step 5: 最终 Commit**
|
||
|
||
```bash
|
||
git log --oneline -20
|
||
```
|
||
|
||
确认所有 benchmark 相关 commit 都在。
|
||
|
||
---
|
||
|
||
## 附录:常用命令速查
|
||
|
||
```bash
|
||
# 运行所有微基准
|
||
go test -bench=. -benchmem ./internal/benchmark/micro/...
|
||
|
||
# 运行单个基准
|
||
go test -bench=BenchmarkCacheGet -benchmem ./internal/benchmark/micro/...
|
||
|
||
# 对比两个基准结果
|
||
benchstat old.txt new.txt
|
||
|
||
# 查看 CPU profile
|
||
go tool pprof -http=:8081 benchmarks/v0.4.0/pprof/cpu.prof
|
||
|
||
# 查看内存分配
|
||
go tool pprof -http=:8081 benchmarks/v0.4.0/pprof/allocs.prof
|
||
|
||
# 生成火焰图
|
||
go tool pprof -png benchmarks/v0.4.0/pprof/cpu.prof > cpu-flamegraph.png
|
||
|
||
# 系统压测
|
||
make bench-system
|
||
|
||
# 性能回归检测
|
||
make bench-compare
|
||
```
|
||
|
||
---
|
||
|
||
## Spec Coverage Check
|
||
|
||
| Spec Section | Task |
|
||
|-------------|------|
|
||
| 建立 benchmark 目录结构 | Task 1 |
|
||
| 补充 resolver 微基准 | Task 2 |
|
||
| 补充 stream 微基准 | Task 3 |
|
||
| 补充 cache 微基准 | Task 4 |
|
||
| 补充 lua 微基准 | Task 5 |
|
||
| 集成基准测试 | Task 6 |
|
||
| 系统压测脚本 | Task 7 |
|
||
| 一键基准脚本 | Task 8 |
|
||
| 生成 baseline | Task 9 |
|
||
| 采集 pprof | Task 10 |
|
||
| 分析报告 | Task 11 |
|
||
| 实施优化 | Task 12 |
|
||
| 回归检测 | Task 13 |
|
||
| 最终验证 | Task 14 |
|