172 lines
4.1 KiB
Go
172 lines
4.1 KiB
Go
package luajit_bench
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"testing"
|
|
)
|
|
|
|
// Profiling flags
|
|
var (
|
|
cpuProfile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
|
memProfile = flag.String("memprofile", "", "write memory profile to `file`")
|
|
memProfileGC = flag.Bool("memprofilegc", false, "force GC before writing memory profile")
|
|
blockProfile = flag.String("blockprofile", "", "write block profile to `file`")
|
|
mutexProfile = flag.String("mutexprofile", "", "write mutex profile to `file`")
|
|
)
|
|
|
|
// setupTestMain configures profiling for benchmarks
|
|
func setupTestMain() {
|
|
// Make sure the flags are parsed
|
|
if !flag.Parsed() {
|
|
flag.Parse()
|
|
}
|
|
|
|
// CPU profiling
|
|
if *cpuProfile != "" {
|
|
f, err := os.Create(*cpuProfile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to create CPU profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if err := pprof.StartCPUProfile(f); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to start CPU profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("CPU profiling enabled")
|
|
}
|
|
|
|
// Block profiling (goroutine blocking)
|
|
if *blockProfile != "" {
|
|
runtime.SetBlockProfileRate(1)
|
|
fmt.Println("Block profiling enabled")
|
|
}
|
|
|
|
// Mutex profiling (lock contention)
|
|
if *mutexProfile != "" {
|
|
runtime.SetMutexProfileFraction(1)
|
|
fmt.Println("Mutex profiling enabled")
|
|
}
|
|
}
|
|
|
|
// teardownTestMain completes profiling and writes output files
|
|
func teardownTestMain() {
|
|
// Stop CPU profile
|
|
if *cpuProfile != "" {
|
|
pprof.StopCPUProfile()
|
|
fmt.Println("CPU profile written to", *cpuProfile)
|
|
}
|
|
|
|
// Write memory profile
|
|
if *memProfile != "" {
|
|
f, err := os.Create(*memProfile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to create memory profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer f.Close()
|
|
|
|
// Force garbage collection before writing memory profile if requested
|
|
if *memProfileGC {
|
|
runtime.GC()
|
|
}
|
|
|
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to write memory profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("Memory profile written to", *memProfile)
|
|
}
|
|
|
|
// Write block profile
|
|
if *blockProfile != "" {
|
|
f, err := os.Create(*blockProfile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to create block profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer f.Close()
|
|
|
|
if err := pprof.Lookup("block").WriteTo(f, 0); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to write block profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("Block profile written to", *blockProfile)
|
|
}
|
|
|
|
// Write mutex profile
|
|
if *mutexProfile != "" {
|
|
f, err := os.Create(*mutexProfile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to create mutex profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer f.Close()
|
|
|
|
if err := pprof.Lookup("mutex").WriteTo(f, 0); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to write mutex profile: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("Mutex profile written to", *mutexProfile)
|
|
}
|
|
}
|
|
|
|
// TestMain is the entry point for all tests in this package
|
|
func TestMain(m *testing.M) {
|
|
setupTestMain()
|
|
code := m.Run()
|
|
teardownTestMain()
|
|
os.Exit(code)
|
|
}
|
|
|
|
// MemStats captures a snapshot of memory statistics
|
|
type MemStats struct {
|
|
Alloc uint64
|
|
TotalAlloc uint64
|
|
Sys uint64
|
|
Mallocs uint64
|
|
Frees uint64
|
|
HeapAlloc uint64
|
|
}
|
|
|
|
// CaptureMemStats returns current memory statistics
|
|
func CaptureMemStats() MemStats {
|
|
var m runtime.MemStats
|
|
runtime.ReadMemStats(&m)
|
|
return MemStats{
|
|
Alloc: m.Alloc,
|
|
TotalAlloc: m.TotalAlloc,
|
|
Sys: m.Sys,
|
|
Mallocs: m.Mallocs,
|
|
Frees: m.Frees,
|
|
HeapAlloc: m.HeapAlloc,
|
|
}
|
|
}
|
|
|
|
// TrackMemoryUsage runs fn and reports memory usage before and after
|
|
func TrackMemoryUsage(b *testing.B, name string, fn func()) {
|
|
b.Helper()
|
|
|
|
// Force GC before measurement
|
|
runtime.GC()
|
|
|
|
// Capture memory stats before
|
|
before := CaptureMemStats()
|
|
|
|
// Run the function
|
|
fn()
|
|
|
|
// Force GC after measurement to get accurate stats
|
|
runtime.GC()
|
|
|
|
// Capture memory stats after
|
|
after := CaptureMemStats()
|
|
|
|
// Report stats
|
|
b.ReportMetric(float64(after.Mallocs-before.Mallocs), name+"-mallocs")
|
|
b.ReportMetric(float64(after.TotalAlloc-before.TotalAlloc)/float64(b.N), name+"-bytes/op")
|
|
}
|