Memory Layout Optimization
Comprehensive guide to optimizing memory layout for performance in Go applications. This guide covers struct field ordering, memory alignment, cache-friendly data structures, and memory locality optimization.
Table of Contents
- Introduction
- Memory Alignment Fundamentals
- Struct Field Ordering
- Cache-Friendly Data Structures
- Memory Locality Optimization
- NUMA Considerations
- Performance Analysis
- Optimization Tools
- Best Practices
Introduction
Memory layout optimization focuses on arranging data structures to maximize CPU cache efficiency, minimize memory footprint, and improve access patterns. Proper memory layout can dramatically improve application performance.
Memory Layout Framework
package main
import (
"fmt"
"reflect"
"runtime"
"sort"
"sync"
"sync/atomic"
"time"
"unsafe"
)
// MemoryLayoutAnalyzer provides comprehensive memory layout analysis
type MemoryLayoutAnalyzer struct {
config LayoutConfig
structures map[string]*StructureInfo
analyzer *AlignmentAnalyzer
optimizer *LayoutOptimizer
profiler *CacheProfiler
metrics *LayoutMetrics
mu sync.RWMutex
}
// LayoutConfig contains memory layout analysis configuration
type LayoutConfig struct {
TargetArch string
CacheLineSize int
PageSize int
EnableProfiling bool
OptimizeForCache bool
OptimizeForSize bool
AnalyzeDepth int
}
// StructureInfo contains information about data structure layout
type StructureInfo struct {
Name string
Type reflect.Type
Size uintptr
Alignment uintptr
Fields []FieldInfo
Padding []PaddingInfo
CacheLines int
Fragmentation float64
Hotness map[string]int64
AccessPattern AccessPattern
}
// FieldInfo contains information about struct fields
type FieldInfo struct {
Name string
Type reflect.Type
Size uintptr
Offset uintptr
Alignment uintptr
Tag string
IsExported bool
IsPointer bool
AccessFreq int64
}
// PaddingInfo contains information about padding bytes
type PaddingInfo struct {
Offset uintptr
Size uintptr
Reason string
}
// AccessPattern describes how data is accessed
type AccessPattern struct {
Sequential bool
Random bool
Temporal bool
Spatial bool
ReadHeavy bool
WriteHeavy bool
HotFields []string
ColdFields []string
}
// AlignmentAnalyzer analyzes memory alignment
type AlignmentAnalyzer struct {
rules []AlignmentRule
violations []AlignmentViolation
suggestions []AlignmentSuggestion
}
// AlignmentRule defines alignment rules
type AlignmentRule struct {
TypePattern string
MinAlignment uintptr
MaxAlignment uintptr
Description string
}
// AlignmentViolation represents an alignment issue
type AlignmentViolation struct {
StructName string
FieldName string
Expected uintptr
Actual uintptr
Impact ViolationImpact
Severity ViolationSeverity
}
// ViolationImpact measures the impact of alignment violations
type ViolationImpact struct {
MemoryWaste uintptr
CacheEfficiency float64
PerformanceHit float64
}
// ViolationSeverity defines violation severity levels
type ViolationSeverity int
const (
LowSeverity ViolationSeverity = iota
MediumSeverity
HighSeverity
CriticalSeverity
)
// AlignmentSuggestion provides optimization suggestions
type AlignmentSuggestion struct {
StructName string
Optimization OptimizationType
Description string
ExpectedGain OptimizationGain
Implementation string
Example string
}
// OptimizationType defines optimization types
type OptimizationType int
const (
FieldReordering OptimizationType = iota
StructPacking
CacheAlignment
MemoryPooling
DataLocality
PaddingReduction
)
// OptimizationGain measures expected optimization gains
type OptimizationGain struct {
SizeReduction uintptr
CacheImprovement float64
MemoryBandwidth float64
LatencyReduction float64
}
// LayoutOptimizer optimizes memory layouts
type LayoutOptimizer struct {
strategies []OptimizationStrategy
rules []OptimizationRule
results map[string]*OptimizationResult
}
// OptimizationStrategy defines layout optimization strategies
type OptimizationStrategy interface {
Analyze(info *StructureInfo) *OptimizationResult
Apply(structDef string) (string, error)
Estimate(info *StructureInfo) OptimizationGain
}
// OptimizationRule defines optimization rules
type OptimizationRule struct {
Pattern string
Action string
Priority int
Conditions []string
Description string
}
// OptimizationResult contains optimization results
type OptimizationResult struct {
OriginalSize uintptr
OptimizedSize uintptr
SizeReduction uintptr
FieldOrder []string
PaddingReduced uintptr
CacheEfficiency float64
Implementation string
}
// CacheProfiler profiles cache behavior
type CacheProfiler struct {
config CacheConfig
metrics *CacheMetrics
hotspots []CacheHotspot
missPatterns []MissPattern
}
// CacheConfig contains cache profiling configuration
type CacheConfig struct {
L1CacheSize int
L2CacheSize int
L3CacheSize int
LineSize int
Associativity int
EnableTracing bool
}
// CacheMetrics tracks cache performance
type CacheMetrics struct {
L1Hits int64
L1Misses int64
L2Hits int64
L2Misses int64
L3Hits int64
L3Misses int64
CacheMissRate float64
MemoryLatency time.Duration
Bandwidth float64
}
// CacheHotspot identifies cache performance hotspots
type CacheHotspot struct {
Address uintptr
StructName string
FieldName string
MissRate float64
AccessCount int64
Impact CacheImpact
}
// CacheImpact measures cache performance impact
type CacheImpact struct {
LatencyIncrease time.Duration
BandwidthReduction float64
ThroughputImpact float64
}
// MissPattern identifies cache miss patterns
type MissPattern struct {
Pattern string
Frequency int64
StructTypes []string
Mitigation string
}
// LayoutMetrics tracks overall layout performance
type LayoutMetrics struct {
StructuresAnalyzed int64
TotalSizeReduction uintptr
AverageFragmentation float64
CacheEfficiency float64
MemoryBandwidth float64
OptimizationsApplied int64
}
// NewMemoryLayoutAnalyzer creates a new memory layout analyzer
func NewMemoryLayoutAnalyzer(config LayoutConfig) *MemoryLayoutAnalyzer {
return &MemoryLayoutAnalyzer{
config: config,
structures: make(map[string]*StructureInfo),
analyzer: NewAlignmentAnalyzer(),
optimizer: NewLayoutOptimizer(),
profiler: NewCacheProfiler(config),
metrics: &LayoutMetrics{},
}
}
// AnalyzeStructure analyzes a struct's memory layout
func (mla *MemoryLayoutAnalyzer) AnalyzeStructure(structType reflect.Type) (*StructureInfo, error) {
if structType.Kind() != reflect.Struct {
return nil, fmt.Errorf("type %s is not a struct", structType.Name())
}
info := &StructureInfo{
Name: structType.Name(),
Type: structType,
Size: structType.Size(),
Alignment: uintptr(structType.Align()),
Fields: make([]FieldInfo, 0),
Padding: make([]PaddingInfo, 0),
Hotness: make(map[string]int64),
}
// Analyze fields
if err := mla.analyzeFields(info, structType); err != nil {
return nil, fmt.Errorf("field analysis failed: %w", err)
}
// Calculate padding
mla.calculatePadding(info)
// Analyze cache behavior
mla.analyzeCacheBehavior(info)
// Calculate fragmentation
info.Fragmentation = mla.calculateFragmentation(info)
// Store structure info
mla.mu.Lock()
mla.structures[info.Name] = info
atomic.AddInt64(&mla.metrics.StructuresAnalyzed, 1)
mla.mu.Unlock()
return info, nil
}
// analyzeFields analyzes struct fields
func (mla *MemoryLayoutAnalyzer) analyzeFields(info *StructureInfo, structType reflect.Type) error {
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
fieldInfo := FieldInfo{
Name: field.Name,
Type: field.Type,
Size: field.Type.Size(),
Offset: field.Offset,
Alignment: uintptr(field.Type.Align()),
Tag: string(field.Tag),
IsExported: field.PkgPath == "",
IsPointer: field.Type.Kind() == reflect.Ptr,
}
info.Fields = append(info.Fields, fieldInfo)
}
return nil
}
// calculatePadding calculates padding bytes in the struct
func (mla *MemoryLayoutAnalyzer) calculatePadding(info *StructureInfo) {
if len(info.Fields) == 0 {
return
}
// Sort fields by offset
fields := make([]FieldInfo, len(info.Fields))
copy(fields, info.Fields)
sort.Slice(fields, func(i, j int) bool {
return fields[i].Offset < fields[j].Offset
})
currentOffset := uintptr(0)
for i, field := range fields {
// Check for padding before field
if field.Offset > currentOffset {
padding := PaddingInfo{
Offset: currentOffset,
Size: field.Offset - currentOffset,
Reason: "alignment",
}
info.Padding = append(info.Padding, padding)
}
currentOffset = field.Offset + field.Size
// Check for padding at end of struct
if i == len(fields)-1 && currentOffset < info.Size {
padding := PaddingInfo{
Offset: currentOffset,
Size: info.Size - currentOffset,
Reason: "struct_alignment",
}
info.Padding = append(info.Padding, padding)
}
}
}
// analyzeCacheBehavior analyzes cache behavior of the structure
func (mla *MemoryLayoutAnalyzer) analyzeCacheBehavior(info *StructureInfo) {
cacheLineSize := uintptr(mla.config.CacheLineSize)
if cacheLineSize == 0 {
cacheLineSize = 64 // Default cache line size
}
// Calculate number of cache lines
info.CacheLines = int((info.Size + cacheLineSize - 1) / cacheLineSize)
// Analyze field distribution across cache lines
for i, field := range info.Fields {
fieldStart := field.Offset
fieldEnd := field.Offset + field.Size
startCacheLine := fieldStart / cacheLineSize
endCacheLine := (fieldEnd - 1) / cacheLineSize
// Field spans multiple cache lines
if startCacheLine != endCacheLine {
info.Fields[i].AccessFreq = -1 // Mark as potentially problematic
}
}
}
// calculateFragmentation calculates memory fragmentation
func (mla *MemoryLayoutAnalyzer) calculateFragmentation(info *StructureInfo) float64 {
totalPadding := uintptr(0)
for _, padding := range info.Padding {
totalPadding += padding.Size
}
if info.Size == 0 {
return 0
}
return float64(totalPadding) / float64(info.Size)
}
// NewAlignmentAnalyzer creates a new alignment analyzer
func NewAlignmentAnalyzer() *AlignmentAnalyzer {
analyzer := &AlignmentAnalyzer{
rules: make([]AlignmentRule, 0),
violations: make([]AlignmentViolation, 0),
suggestions: make([]AlignmentSuggestion, 0),
}
// Initialize alignment rules
analyzer.initializeRules()
return analyzer
}
// initializeRules initializes alignment rules
func (aa *AlignmentAnalyzer) initializeRules() {
// Common alignment rules
aa.rules = append(aa.rules, AlignmentRule{
TypePattern: "int64|uint64|float64",
MinAlignment: 8,
MaxAlignment: 8,
Description: "64-bit types should be 8-byte aligned",
})
aa.rules = append(aa.rules, AlignmentRule{
TypePattern: "int32|uint32|float32",
MinAlignment: 4,
MaxAlignment: 4,
Description: "32-bit types should be 4-byte aligned",
})
aa.rules = append(aa.rules, AlignmentRule{
TypePattern: "int16|uint16",
MinAlignment: 2,
MaxAlignment: 2,
Description: "16-bit types should be 2-byte aligned",
})
aa.rules = append(aa.rules, AlignmentRule{
TypePattern: "\\*",
MinAlignment: 8,
MaxAlignment: 8,
Description: "Pointers should be 8-byte aligned on 64-bit systems",
})
}
// AnalyzeAlignment analyzes struct alignment
func (aa *AlignmentAnalyzer) AnalyzeAlignment(info *StructureInfo) error {
aa.violations = aa.violations[:0] // Clear previous violations
for _, field := range info.Fields {
if violation := aa.checkFieldAlignment(info.Name, field); violation != nil {
aa.violations = append(aa.violations, *violation)
}
}
// Generate suggestions based on violations
aa.generateSuggestions(info)
return nil
}
// checkFieldAlignment checks if a field violates alignment rules
func (aa *AlignmentAnalyzer) checkFieldAlignment(structName string, field FieldInfo) *AlignmentViolation {
expectedAlignment := aa.getExpectedAlignment(field.Type)
if expectedAlignment > 0 && field.Offset%expectedAlignment != 0 {
impact := ViolationImpact{
MemoryWaste: expectedAlignment - (field.Offset % expectedAlignment),
CacheEfficiency: 0.9, // Simplified calculation
PerformanceHit: 0.1, // Simplified calculation
}
return &AlignmentViolation{
StructName: structName,
FieldName: field.Name,
Expected: expectedAlignment,
Actual: field.Offset % expectedAlignment,
Impact: impact,
Severity: aa.calculateSeverity(impact),
}
}
return nil
}
// getExpectedAlignment returns expected alignment for a type
func (aa *AlignmentAnalyzer) getExpectedAlignment(t reflect.Type) uintptr {
switch t.Kind() {
case reflect.Int64, reflect.Uint64, reflect.Float64:
return 8
case reflect.Int32, reflect.Uint32, reflect.Float32:
return 4
case reflect.Int16, reflect.Uint16:
return 2
case reflect.Ptr, reflect.UnsafePointer:
return 8 // Assuming 64-bit system
case reflect.Struct:
return uintptr(t.Align())
default:
return 1
}
}
// calculateSeverity calculates violation severity
func (aa *AlignmentAnalyzer) calculateSeverity(impact ViolationImpact) ViolationSeverity {
if impact.PerformanceHit > 0.2 {
return CriticalSeverity
} else if impact.PerformanceHit > 0.1 {
return HighSeverity
} else if impact.PerformanceHit > 0.05 {
return MediumSeverity
}
return LowSeverity
}
// generateSuggestions generates optimization suggestions
func (aa *AlignmentAnalyzer) generateSuggestions(info *StructureInfo) {
aa.suggestions = aa.suggestions[:0] // Clear previous suggestions
// Suggest field reordering if there are violations
if len(aa.violations) > 0 {
suggestion := AlignmentSuggestion{
StructName: info.Name,
Optimization: FieldReordering,
Description: "Reorder fields to reduce padding and improve alignment",
ExpectedGain: aa.calculateReorderingGain(info),
Implementation: "Order fields by size (largest first) or by access pattern",
Example: aa.generateReorderingExample(info),
}
aa.suggestions = append(aa.suggestions, suggestion)
}
// Suggest struct packing if fragmentation is high
if info.Fragmentation > 0.2 {
suggestion := AlignmentSuggestion{
StructName: info.Name,
Optimization: StructPacking,
Description: "Use struct tags or bit fields to reduce memory usage",
ExpectedGain: aa.calculatePackingGain(info),
Implementation: "Use smaller types or bit fields where appropriate",
Example: "type OptimizedStruct struct { flags uint8; id uint16; data uint32 }",
}
aa.suggestions = append(aa.suggestions, suggestion)
}
}
// calculateReorderingGain calculates expected gain from field reordering
func (aa *AlignmentAnalyzer) calculateReorderingGain(info *StructureInfo) OptimizationGain {
// Simulate optimal field ordering
optimizedSize := aa.calculateOptimalSize(info)
return OptimizationGain{
SizeReduction: info.Size - optimizedSize,
CacheImprovement: 0.15, // Estimated
MemoryBandwidth: 0.1, // Estimated
LatencyReduction: 0.05, // Estimated
}
}
// calculateOptimalSize calculates optimal struct size with field reordering
func (aa *AlignmentAnalyzer) calculateOptimalSize(info *StructureInfo) uintptr {
// Sort fields by alignment requirements (largest first)
fields := make([]FieldInfo, len(info.Fields))
copy(fields, info.Fields)
sort.Slice(fields, func(i, j int) bool {
return fields[i].Alignment > fields[j].Alignment
})
currentOffset := uintptr(0)
maxAlignment := uintptr(1)
for _, field := range fields {
// Align field
if field.Alignment > maxAlignment {
maxAlignment = field.Alignment
}
aligned := (currentOffset + field.Alignment - 1) &^ (field.Alignment - 1)
currentOffset = aligned + field.Size
}
// Align struct to maximum field alignment
finalSize := (currentOffset + maxAlignment - 1) &^ (maxAlignment - 1)
return finalSize
}
// calculatePackingGain calculates expected gain from struct packing
func (aa *AlignmentAnalyzer) calculatePackingGain(info *StructureInfo) OptimizationGain {
// Estimate packing savings based on current fragmentation
estimatedSaving := uintptr(float64(info.Size) * info.Fragmentation * 0.5)
return OptimizationGain{
SizeReduction: estimatedSaving,
CacheImprovement: 0.1,
MemoryBandwidth: 0.05,
LatencyReduction: 0.02,
}
}
// generateReorderingExample generates a field reordering example
func (aa *AlignmentAnalyzer) generateReorderingExample(info *StructureInfo) string {
// Sort fields by alignment (largest first)
fields := make([]FieldInfo, len(info.Fields))
copy(fields, info.Fields)
sort.Slice(fields, func(i, j int) bool {
return fields[i].Alignment > fields[j].Alignment
})
example := fmt.Sprintf("type %s struct {\n", info.Name)
for _, field := range fields {
example += fmt.Sprintf(" %s %s\n", field.Name, field.Type.String())
}
example += "}"
return example
}
// NewLayoutOptimizer creates a new layout optimizer
func NewLayoutOptimizer() *LayoutOptimizer {
optimizer := &LayoutOptimizer{
strategies: make([]OptimizationStrategy, 0),
rules: make([]OptimizationRule, 0),
results: make(map[string]*OptimizationResult),
}
// Initialize optimization strategies
optimizer.initializeStrategies()
return optimizer
}
// initializeStrategies initializes optimization strategies
func (lo *LayoutOptimizer) initializeStrategies() {
lo.strategies = append(lo.strategies, &FieldReorderingStrategy{})
lo.strategies = append(lo.strategies, &CacheAlignmentStrategy{})
lo.strategies = append(lo.strategies, &PaddingReductionStrategy{})
}
// OptimizeLayout optimizes struct layout
func (lo *LayoutOptimizer) OptimizeLayout(info *StructureInfo) (*OptimizationResult, error) {
var bestResult *OptimizationResult
var bestSavings uintptr
// Try each optimization strategy
for _, strategy := range lo.strategies {
result := strategy.Analyze(info)
if result != nil && result.SizeReduction > bestSavings {
bestResult = result
bestSavings = result.SizeReduction
}
}
if bestResult != nil {
lo.results[info.Name] = bestResult
}
return bestResult, nil
}
// FieldReorderingStrategy optimizes field ordering
type FieldReorderingStrategy struct{}
// Analyze analyzes field reordering opportunities
func (frs *FieldReorderingStrategy) Analyze(info *StructureInfo) *OptimizationResult {
// Calculate optimal field order
optimizedFields := frs.calculateOptimalOrder(info.Fields)
optimizedSize := frs.calculateSizeWithOrder(optimizedFields)
if optimizedSize >= info.Size {
return nil // No improvement
}
fieldOrder := make([]string, len(optimizedFields))
for i, field := range optimizedFields {
fieldOrder[i] = field.Name
}
return &OptimizationResult{
OriginalSize: info.Size,
OptimizedSize: optimizedSize,
SizeReduction: info.Size - optimizedSize,
FieldOrder: fieldOrder,
CacheEfficiency: 0.15, // Estimated improvement
Implementation: frs.generateImplementation(optimizedFields, info.Name),
}
}
// calculateOptimalOrder calculates optimal field ordering
func (frs *FieldReorderingStrategy) calculateOptimalOrder(fields []FieldInfo) []FieldInfo {
// Sort by alignment requirements (largest first), then by size
optimized := make([]FieldInfo, len(fields))
copy(optimized, fields)
sort.Slice(optimized, func(i, j int) bool {
if optimized[i].Alignment != optimized[j].Alignment {
return optimized[i].Alignment > optimized[j].Alignment
}
return optimized[i].Size > optimized[j].Size
})
return optimized
}
// calculateSizeWithOrder calculates struct size with given field order
func (frs *FieldReorderingStrategy) calculateSizeWithOrder(fields []FieldInfo) uintptr {
currentOffset := uintptr(0)
maxAlignment := uintptr(1)
for _, field := range fields {
if field.Alignment > maxAlignment {
maxAlignment = field.Alignment
}
// Align field
aligned := (currentOffset + field.Alignment - 1) &^ (field.Alignment - 1)
currentOffset = aligned + field.Size
}
// Align struct to maximum field alignment
finalSize := (currentOffset + maxAlignment - 1) &^ (maxAlignment - 1)
return finalSize
}
// generateImplementation generates optimized struct implementation
func (frs *FieldReorderingStrategy) generateImplementation(fields []FieldInfo, structName string) string {
impl := fmt.Sprintf("type %s struct {\n", structName)
for _, field := range fields {
impl += fmt.Sprintf(" %s %s", field.Name, field.Type.String())
if field.Tag != "" {
impl += fmt.Sprintf(" `%s`", field.Tag)
}
impl += "\n"
}
impl += "}"
return impl
}
// Apply applies field reordering optimization
func (frs *FieldReorderingStrategy) Apply(structDef string) (string, error) {
// This would implement actual code transformation
return structDef, nil
}
// Estimate estimates optimization impact
func (frs *FieldReorderingStrategy) Estimate(info *StructureInfo) OptimizationGain {
optimizedFields := frs.calculateOptimalOrder(info.Fields)
optimizedSize := frs.calculateSizeWithOrder(optimizedFields)
return OptimizationGain{
SizeReduction: info.Size - optimizedSize,
CacheImprovement: 0.15,
MemoryBandwidth: 0.1,
LatencyReduction: 0.05,
}
}
// CacheAlignmentStrategy optimizes for cache alignment
type CacheAlignmentStrategy struct{}
// Analyze analyzes cache alignment opportunities
func (cas *CacheAlignmentStrategy) Analyze(info *StructureInfo) *OptimizationResult {
cacheLineSize := uintptr(64) // Typical cache line size
// Check if struct would benefit from cache line alignment
if info.Size <= cacheLineSize && info.CacheLines > 1 {
// Struct spans multiple cache lines but is small enough to fit in one
paddedSize := cacheLineSize
return &OptimizationResult{
OriginalSize: info.Size,
OptimizedSize: paddedSize,
SizeReduction: 0, // Size increases but cache efficiency improves
CacheEfficiency: 0.3, // Significant cache improvement
Implementation: cas.generateCacheAlignedStruct(info, cacheLineSize),
}
}
return nil
}
// generateCacheAlignedStruct generates cache-aligned struct
func (cas *CacheAlignmentStrategy) generateCacheAlignedStruct(info *StructureInfo, cacheLineSize uintptr) string {
return fmt.Sprintf("type %s struct {\n // Cache-aligned struct\n %s\n} // Size: %d bytes, Cache-aligned\n",
info.Name, "/* fields */", cacheLineSize)
}
// Apply applies cache alignment optimization
func (cas *CacheAlignmentStrategy) Apply(structDef string) (string, error) {
return structDef, nil
}
// Estimate estimates cache alignment impact
func (cas *CacheAlignmentStrategy) Estimate(info *StructureInfo) OptimizationGain {
return OptimizationGain{
SizeReduction: 0, // May increase size
CacheImprovement: 0.3,
MemoryBandwidth: 0.2,
LatencyReduction: 0.15,
}
}
// PaddingReductionStrategy reduces padding
type PaddingReductionStrategy struct{}
// Analyze analyzes padding reduction opportunities
func (prs *PaddingReductionStrategy) Analyze(info *StructureInfo) *OptimizationResult {
totalPadding := uintptr(0)
for _, padding := range info.Padding {
totalPadding += padding.Size
}
if totalPadding == 0 {
return nil // No padding to reduce
}
// Estimate achievable padding reduction
achievableReduction := totalPadding * 70 / 100 // Assume 70% reduction possible
return &OptimizationResult{
OriginalSize: info.Size,
OptimizedSize: info.Size - achievableReduction,
SizeReduction: achievableReduction,
PaddingReduced: achievableReduction,
CacheEfficiency: 0.1,
Implementation: "Reorder fields and use appropriate types",
}
}
// Apply applies padding reduction optimization
func (prs *PaddingReductionStrategy) Apply(structDef string) (string, error) {
return structDef, nil
}
// Estimate estimates padding reduction impact
func (prs *PaddingReductionStrategy) Estimate(info *StructureInfo) OptimizationGain {
totalPadding := uintptr(0)
for _, padding := range info.Padding {
totalPadding += padding.Size
}
return OptimizationGain{
SizeReduction: totalPadding * 70 / 100,
CacheImprovement: 0.1,
MemoryBandwidth: 0.05,
LatencyReduction: 0.02,
}
}
// NewCacheProfiler creates a new cache profiler
func NewCacheProfiler(config LayoutConfig) *CacheProfiler {
cacheConfig := CacheConfig{
L1CacheSize: 32 * 1024, // 32KB
L2CacheSize: 256 * 1024, // 256KB
L3CacheSize: 8 * 1024 * 1024, // 8MB
LineSize: 64,
Associativity: 8,
EnableTracing: config.EnableProfiling,
}
return &CacheProfiler{
config: cacheConfig,
metrics: &CacheMetrics{},
hotspots: make([]CacheHotspot, 0),
missPatterns: make([]MissPattern, 0),
}
}
// ProfileCacheBehavior profiles cache behavior for a structure
func (cp *CacheProfiler) ProfileCacheBehavior(info *StructureInfo) error {
// Simulate cache behavior analysis
cp.analyzeCacheLineUsage(info)
cp.identifyHotspots(info)
cp.detectMissPatterns(info)
return nil
}
// analyzeCacheLineUsage analyzes cache line usage
func (cp *CacheProfiler) analyzeCacheLineUsage(info *StructureInfo) {
cacheLineSize := uintptr(cp.config.LineSize)
for _, field := range info.Fields {
fieldStart := field.Offset
fieldEnd := field.Offset + field.Size
startLine := fieldStart / cacheLineSize
endLine := (fieldEnd - 1) / cacheLineSize
if startLine != endLine {
// Field spans multiple cache lines - potential hotspot
hotspot := CacheHotspot{
Address: fieldStart,
StructName: info.Name,
FieldName: field.Name,
MissRate: 0.2, // Estimated
AccessCount: 1000, // Estimated
Impact: CacheImpact{
LatencyIncrease: 100 * time.Nanosecond,
BandwidthReduction: 0.15,
ThroughputImpact: 0.1,
},
}
cp.hotspots = append(cp.hotspots, hotspot)
}
}
}
// identifyHotspots identifies cache performance hotspots
func (cp *CacheProfiler) identifyHotspots(info *StructureInfo) {
// Analyze access patterns and identify frequently accessed fields
// that may cause cache issues
for _, field := range info.Fields {
if field.AccessFreq > 1000 { // High frequency access
hotspot := CacheHotspot{
Address: field.Offset,
StructName: info.Name,
FieldName: field.Name,
MissRate: 0.1, // Estimated
AccessCount: field.AccessFreq,
Impact: CacheImpact{
LatencyIncrease: 50 * time.Nanosecond,
BandwidthReduction: 0.05,
ThroughputImpact: 0.03,
},
}
cp.hotspots = append(cp.hotspots, hotspot)
}
}
}
// detectMissPatterns detects cache miss patterns
func (cp *CacheProfiler) detectMissPatterns(info *StructureInfo) {
// Analyze struct layout for common miss patterns
if info.CacheLines > 2 {
pattern := MissPattern{
Pattern: "large_struct",
Frequency: 100,
StructTypes: []string{info.Name},
Mitigation: "Split large struct into smaller, frequently-accessed parts",
}
cp.missPatterns = append(cp.missPatterns, pattern)
}
if info.Fragmentation > 0.3 {
pattern := MissPattern{
Pattern: "fragmented_layout",
Frequency: 50,
StructTypes: []string{info.Name},
Mitigation: "Reorder fields to reduce padding and improve locality",
}
cp.missPatterns = append(cp.missPatterns, pattern)
}
}
// GetOptimizationSummary returns a summary of optimization opportunities
func (mla *MemoryLayoutAnalyzer) GetOptimizationSummary() *OptimizationSummary {
mla.mu.RLock()
defer mla.mu.RUnlock()
summary := &OptimizationSummary{
TotalStructures: len(mla.structures),
OptimizationGains: make(map[string]OptimizationGain),
Suggestions: make([]AlignmentSuggestion, 0),
CacheHotspots: len(mla.profiler.hotspots),
TotalSizeReduction: 0,
}
for name, info := range mla.structures {
if result, err := mla.optimizer.OptimizeLayout(info); err == nil && result != nil {
summary.OptimizationGains[name] = OptimizationGain{
SizeReduction: result.SizeReduction,
CacheImprovement: result.CacheEfficiency,
}
summary.TotalSizeReduction += result.SizeReduction
}
// Add analyzer suggestions
if err := mla.analyzer.AnalyzeAlignment(info); err == nil {
summary.Suggestions = append(summary.Suggestions, mla.analyzer.suggestions...)
}
}
return summary
}
// OptimizationSummary contains optimization summary
type OptimizationSummary struct {
TotalStructures int
OptimizationGains map[string]OptimizationGain
Suggestions []AlignmentSuggestion
CacheHotspots int
TotalSizeReduction uintptr
}
// Example usage
func ExampleMemoryLayoutAnalysis() {
config := LayoutConfig{
TargetArch: "amd64",
CacheLineSize: 64,
PageSize: 4096,
EnableProfiling: true,
OptimizeForCache: true,
OptimizeForSize: true,
AnalyzeDepth: 3,
}
analyzer := NewMemoryLayoutAnalyzer(config)
// Example struct to analyze
type ExampleStruct struct {
A int8 // 1 byte
B int64 // 8 bytes - will cause padding after A
C int16 // 2 bytes
D int32 // 4 bytes
E int8 // 1 byte - will cause padding at end
}
structType := reflect.TypeOf(ExampleStruct{})
info, err := analyzer.AnalyzeStructure(structType)
if err != nil {
fmt.Printf("Analysis failed: %v\n", err)
return
}
fmt.Printf("Original struct size: %d bytes\n", info.Size)
fmt.Printf("Fragmentation: %.2f%%\n", info.Fragmentation*100)
fmt.Printf("Cache lines: %d\n", info.CacheLines)
// Get optimization result
result, err := analyzer.optimizer.OptimizeLayout(info)
if err == nil && result != nil {
fmt.Printf("Optimized size: %d bytes\n", result.OptimizedSize)
fmt.Printf("Size reduction: %d bytes\n", result.SizeReduction)
fmt.Printf("Optimized field order: %v\n", result.FieldOrder)
}
// Get summary
summary := analyzer.GetOptimizationSummary()
fmt.Printf("Total optimization opportunities: %d\n", len(summary.OptimizationGains))
fmt.Printf("Total size reduction: %d bytes\n", summary.TotalSizeReduction)
}
Performance Analysis
Advanced techniques for analyzing memory layout performance impact.
Cache Performance Measurement
Tools and techniques for measuring cache performance.
Memory Bandwidth Analysis
Analyzing memory bandwidth utilization and optimization.
NUMA Optimization
Optimizing data structures for NUMA architectures.
Best Practices
- Field Ordering: Order fields by alignment requirements (largest first)
- Cache Line Awareness: Design structures to fit within cache lines
- Hot/Cold Separation: Separate frequently and infrequently accessed fields
- Avoid False Sharing: Ensure independent data doesn't share cache lines
- Use Appropriate Types: Choose the smallest appropriate data types
- Consider Access Patterns: Optimize layout based on how data is accessed
- Profile Memory Usage: Use tools to measure actual memory behavior
- Test Optimizations: Benchmark before and after layout changes
Summary
Memory layout optimization is crucial for high-performance Go applications:
- Understanding: Know how memory alignment and padding work
- Analysis: Use tools to identify layout inefficiencies
- Optimization: Apply systematic optimization strategies
- Measurement: Profile cache behavior and memory usage
- Validation: Benchmark optimization impact
These techniques enable developers to create memory-efficient data structures that maximize CPU cache utilization and minimize memory footprint.