before merge to main
This commit is contained in:
206
examples/perf/main.go
Normal file
206
examples/perf/main.go
Normal file
@@ -0,0 +1,206 @@
|
||||
// Пример комплексного performance-теста LineDb.
|
||||
// Запуск:
|
||||
//
|
||||
// go run ./examples/perf/main.go
|
||||
//
|
||||
// Сценарий:
|
||||
// 1. Вставка 5000 записей, каждая ~1800 символов.
|
||||
// 2. 10 случайных обновлений, замер среднего времени.
|
||||
// 3. 100 случайных удалений, замер среднего времени.
|
||||
// 4. Замеры памяти процесса через runtime.MemStats.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"linedb/pkg/linedb"
|
||||
)
|
||||
|
||||
const (
|
||||
recordsCount = 5000
|
||||
payloadSize = 1800
|
||||
updateOps = 100
|
||||
deleteOps = 100
|
||||
collectionName = "perf_items"
|
||||
dbDir = "./data/perf-benchmark"
|
||||
allocSizeEstimate = 2048
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if err := os.MkdirAll(dbDir, 0755); err != nil {
|
||||
log.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
|
||||
initOptions := &linedb.LineDbInitOptions{
|
||||
CacheSize: 1000,
|
||||
CacheTTL: time.Minute,
|
||||
DBFolder: dbDir,
|
||||
Collections: []linedb.JSONLFileOptions{
|
||||
{
|
||||
CollectionName: collectionName,
|
||||
AllocSize: allocSizeEstimate,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
db := linedb.NewLineDb(nil)
|
||||
if err := db.Init(true, initOptions); err != nil {
|
||||
log.Fatalf("Init failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
printMem("Before insert")
|
||||
|
||||
fmt.Printf("1) Insert %d records (payload ~%d chars)...\n", recordsCount, payloadSize)
|
||||
start := time.Now()
|
||||
if err := insertRecords(db); err != nil {
|
||||
log.Fatalf("InsertRecords failed: %v", err)
|
||||
}
|
||||
elapsedInsert := time.Since(start)
|
||||
fmt.Printf(" Total insert time: %v, per record: %v\n",
|
||||
elapsedInsert, elapsedInsert/time.Duration(recordsCount))
|
||||
printMem("After insert")
|
||||
|
||||
all, err := db.Read(collectionName, linedb.LineDbAdapterOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("Read after insert failed: %v", err)
|
||||
}
|
||||
fmt.Printf(" Records in collection: %d\n", len(all))
|
||||
|
||||
ids := collectIDs(all)
|
||||
if len(ids) == 0 {
|
||||
log.Fatalf("No IDs collected, cannot continue")
|
||||
}
|
||||
|
||||
fmt.Printf("\n2) Random update of %d records...\n", updateOps)
|
||||
avgUpdate := benchmarkUpdates(db, ids, updateOps)
|
||||
fmt.Printf(" Average update time: %v\n", avgUpdate)
|
||||
printMem("After updates")
|
||||
|
||||
fmt.Printf("\n3) Random delete of %d records...\n", deleteOps)
|
||||
avgDelete := benchmarkDeletes(db, ids, deleteOps)
|
||||
fmt.Printf(" Average delete time: %v\n", avgDelete)
|
||||
printMem("After deletes")
|
||||
|
||||
final, err := db.Read(collectionName, linedb.LineDbAdapterOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("Final read failed: %v", err)
|
||||
}
|
||||
fmt.Printf("\nFinal records in collection: %d\n", len(final))
|
||||
fmt.Printf("Data directory: %s\n", dbDir)
|
||||
}
|
||||
|
||||
func insertRecords(db *linedb.LineDb) error {
|
||||
// Сгенерируем базовый payload
|
||||
base := strings.Repeat("X", payloadSize)
|
||||
|
||||
batch := make([]any, 0, 100)
|
||||
for i := 0; i < recordsCount; i++ {
|
||||
rec := map[string]any{
|
||||
"index": i,
|
||||
"payload": base,
|
||||
"created": time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
batch = append(batch, rec)
|
||||
if len(batch) >= cap(batch) {
|
||||
if err := db.Insert(batch, collectionName, linedb.LineDbAdapterOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
batch = batch[:0]
|
||||
}
|
||||
}
|
||||
if len(batch) > 0 {
|
||||
if err := db.Insert(batch, collectionName, linedb.LineDbAdapterOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectIDs(all []any) []any {
|
||||
ids := make([]any, 0, len(all))
|
||||
for _, r := range all {
|
||||
if m, ok := r.(map[string]any); ok {
|
||||
if id, ok := m["id"]; ok {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func benchmarkUpdates(db *linedb.LineDb, ids []any, ops int) time.Duration {
|
||||
if len(ids) == 0 {
|
||||
return 0
|
||||
}
|
||||
var total time.Duration
|
||||
for i := 0; i < ops; i++ {
|
||||
id := ids[rand.Intn(len(ids))]
|
||||
update := map[string]any{
|
||||
"updatedAt": time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
start := time.Now()
|
||||
_, err := db.Update(update, collectionName, map[string]any{"id": id}, linedb.LineDbAdapterOptions{})
|
||||
dur := time.Since(start)
|
||||
if err != nil {
|
||||
log.Printf("Update error (id=%v): %v", id, err)
|
||||
continue
|
||||
}
|
||||
total += dur
|
||||
}
|
||||
if ops == 0 {
|
||||
return 0
|
||||
}
|
||||
return total / time.Duration(ops)
|
||||
}
|
||||
|
||||
func benchmarkDeletes(db *linedb.LineDb, ids []any, ops int) time.Duration {
|
||||
if len(ids) == 0 {
|
||||
return 0
|
||||
}
|
||||
var total time.Duration
|
||||
used := make(map[int]struct{})
|
||||
for i := 0; i < ops; i++ {
|
||||
// чтобы не удалять по одному и тому же id постоянно, постараемся брать разные индексы
|
||||
var idx int
|
||||
for tries := 0; tries < 10; tries++ {
|
||||
idx = rand.Intn(len(ids))
|
||||
if _, ok := used[idx]; !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
used[idx] = struct{}{}
|
||||
id := ids[idx]
|
||||
|
||||
start := time.Now()
|
||||
_, err := db.Delete(map[string]any{"id": id}, collectionName, linedb.LineDbAdapterOptions{})
|
||||
dur := time.Since(start)
|
||||
if err != nil {
|
||||
log.Printf("Delete error (id=%v): %v", id, err)
|
||||
continue
|
||||
}
|
||||
total += dur
|
||||
}
|
||||
if ops == 0 {
|
||||
return 0
|
||||
}
|
||||
return total / time.Duration(ops)
|
||||
}
|
||||
|
||||
func printMem(label string) {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("\n=== %s ===\n", label)
|
||||
fmt.Printf(" Alloc: %.2f MB\n", float64(m.Alloc)/1024.0/1024.0)
|
||||
fmt.Printf(" TotalAlloc: %.2f MB\n", float64(m.TotalAlloc)/1024.0/1024.0)
|
||||
fmt.Printf(" Sys: %.2f MB\n", float64(m.Sys)/1024.0/1024.0)
|
||||
fmt.Printf(" NumGC: %d\n", m.NumGC)
|
||||
}
|
||||
Reference in New Issue
Block a user