test(cache): FileCache Set 分配热点专项测试
追踪 Set 新建/更新/淘汰三种路径的分配来源: - SetNew: 3 allocs/op (目标 ≤1,待优化) - SetUpdate: 1 allocs/op (已达标) - Eviction: 3 allocs/op (待优化) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1424402d06
commit
41288a560f
238
internal/cache/file_cache_allocation_test.go
vendored
Normal file
238
internal/cache/file_cache_allocation_test.go
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
// Package cache 提供文件缓存分配热点追踪测试。
|
||||
//
|
||||
// 该文件追踪 FileCache.Set 的分配来源,验证池化优化效果。
|
||||
//
|
||||
// 测试场景:
|
||||
// - SetNew: 新建条目路径
|
||||
// - SetUpdate: 更新已存在条目路径
|
||||
// - SetEviction: 淘汰场景(缓存满载)
|
||||
//
|
||||
// 目标:池化后 ≤1 allocs/op
|
||||
//
|
||||
// 作者:xfy
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_New 测试新建条目路径的分配。
|
||||
//
|
||||
// 热点分配来源:
|
||||
// - entry := c.entryPool.Get() -> 池化后 0 allocs
|
||||
// - c.lruList.PushFront(entry) -> list Element 分配
|
||||
// - c.entries[path] = entry -> map 写入
|
||||
func BenchmarkFileCacheSetAllocation_New(b *testing.B) {
|
||||
fc := NewFileCache(10000, 0, 1*time.Hour)
|
||||
|
||||
data := []byte("test data content for benchmark")
|
||||
size := int64(len(data))
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
path := fmt.Sprintf("/new/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_Update 测试更新已存在条目路径的分配。
|
||||
//
|
||||
// 更新路径理论上 0 allocs(不涉及新条目分配)。
|
||||
func BenchmarkFileCacheSetAllocation_Update(b *testing.B) {
|
||||
fc := NewFileCache(10000, 0, 1*time.Hour)
|
||||
|
||||
// 预填充缓存
|
||||
data := []byte("test data content for benchmark")
|
||||
size := int64(len(data))
|
||||
for i := 0; i < 1000; i++ {
|
||||
path := fmt.Sprintf("/update/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
// 循环更新已有条目
|
||||
path := fmt.Sprintf("/update/file%d.txt", i%1000)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_Eviction 测试淘汰场景的分配。
|
||||
//
|
||||
// 热点:
|
||||
// - 淘汰时 removeEntry -> entryPool.Put(entry)
|
||||
// - 新 Set 时 entryPool.Get() 复用
|
||||
// - 理论上 LRU Element 分配仍是热点
|
||||
func BenchmarkFileCacheSetAllocation_Eviction(b *testing.B) {
|
||||
// 容量限制为 100,强制触发淘汰
|
||||
fc := NewFileCache(100, 0, 1*time.Hour)
|
||||
|
||||
// 预填充到容量上限
|
||||
data := []byte("test data content for benchmark")
|
||||
size := int64(len(data))
|
||||
for i := 0; i < 100; i++ {
|
||||
path := fmt.Sprintf("/evict/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
// 每个 Set 都触发淘汰
|
||||
path := fmt.Sprintf("/evict/new%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_EvictionWithPool 测试淘汰+池化复用的分配。
|
||||
//
|
||||
// 验证 entryPool 在淘汰场景的复用效果。
|
||||
func BenchmarkFileCacheSetAllocation_EvictionWithPool(b *testing.B) {
|
||||
fc := NewFileCache(100, 0, 1*time.Hour)
|
||||
|
||||
data := []byte("test data content for benchmark")
|
||||
size := int64(len(data))
|
||||
|
||||
// 预填充
|
||||
for i := 0; i < 100; i++ {
|
||||
path := fmt.Sprintf("/pool/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
path := fmt.Sprintf("/pool/new%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_MemoryLimit 测试内存限制淘汰的分配。
|
||||
//
|
||||
// 热点:evictIfNeeded 需遍历 LRU 链表淘汰多个条目。
|
||||
func BenchmarkFileCacheSetAllocation_MemoryLimit(b *testing.B) {
|
||||
// 限制 1MB,每个条目 1KB,约 1000 条目
|
||||
fc := NewFileCache(0, 1024*1024, 1*time.Hour)
|
||||
|
||||
data := make([]byte, 1024) // 1KB 每条目
|
||||
size := int64(len(data))
|
||||
|
||||
// 预填充到接近上限
|
||||
for i := 0; i < 900; i++ {
|
||||
path := fmt.Sprintf("/mem/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
path := fmt.Sprintf("/mem/new%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_Concurrent 测试并发 Set 的分配。
|
||||
//
|
||||
// 并发场景下锁竞争可能影响池化效果。
|
||||
func BenchmarkFileCacheSetAllocation_Concurrent(b *testing.B) {
|
||||
fc := NewFileCache(10000, 0, 1*time.Hour)
|
||||
|
||||
data := []byte("concurrent test data")
|
||||
size := int64(len(data))
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
path := fmt.Sprintf("/conc/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheSetAllocation_ConcurrentEviction 测试并发淘汰场景。
|
||||
func BenchmarkFileCacheSetAllocation_ConcurrentEviction(b *testing.B) {
|
||||
fc := NewFileCache(100, 0, 1*time.Hour)
|
||||
|
||||
data := []byte("eviction concurrent")
|
||||
size := int64(len(data))
|
||||
|
||||
// 预填充
|
||||
for i := 0; i < 100; i++ {
|
||||
path := fmt.Sprintf("/concevict/file%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
path := fmt.Sprintf("/concevict/new%d.txt", i)
|
||||
fc.Set(path, data, size, time.Now())
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheEntryPool_GetPut 测试 entryPool 直接操作。
|
||||
//
|
||||
// 验证 sync.Pool 的分配效果(应 0 allocs)。
|
||||
func BenchmarkFileCacheEntryPool_GetPut(b *testing.B) {
|
||||
pool := &sync.Pool{
|
||||
New: func() any {
|
||||
return &FileEntry{}
|
||||
},
|
||||
}
|
||||
|
||||
// 预填充池
|
||||
for i := 0; i < 100; i++ {
|
||||
pool.Put(&FileEntry{})
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for b.Loop() {
|
||||
entry := pool.Get().(*FileEntry)
|
||||
// 模拟使用
|
||||
entry.Path = "/test"
|
||||
entry.Data = nil
|
||||
pool.Put(entry)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFileCacheLRUList_PushFront 测试 LRU 链表操作分配。
|
||||
//
|
||||
// 热点:list.PushFront 分配 Element(约 1 allocs)。
|
||||
func BenchmarkFileCacheLRUList_PushFront(b *testing.B) {
|
||||
lruList := list.New()
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; b.Loop(); i++ {
|
||||
entry := &FileEntry{Path: fmt.Sprintf("/lru%d", i)}
|
||||
lruList.PushFront(entry)
|
||||
// 模拟淘汰移除
|
||||
if lruList.Len() > 100 {
|
||||
old := lruList.Back()
|
||||
lruList.Remove(old)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user