go-twig/benchmark/serialization_benchmark.go
semihalev f1add2d820 Implement efficient LRU eviction strategy for attribute cache
- Added tracking of last access time and access count to cache entries
- Implemented eviction policy to remove least recently/frequently used entries
- Cache now removes 10% of entries when the cache is full, prioritizing by usage
- Added benchmarks and tests to verify eviction strategy
- Fixed the previously ineffective eviction strategy
- Improved cache efficiency for applications with many diverse object types

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-11 14:20:47 +03:00

148 lines
4.6 KiB
Go

package main
import (
"bytes"
"encoding/gob"
"fmt"
"time"
"github.com/semihalev/twig"
)
// benchmarkSerialization compares the performance of old and new serialization methods
func benchmarkSerialization() {
fmt.Println("\n=== Template Serialization Benchmark ===")
// Create a more complex template to serialize
engine := twig.New()
source := `
{% extends "base.html" %}
{% block content %}
<h1>{{ title }}</h1>
<ul>
{% for item in items %}
<li>{{ item.name }} - {{ item.value|number_format(2, '.', ',') }}</li>
{% endfor %}
</ul>
{% if showDetails %}
<div class="details">
{% include "details.html" with {'id': user.id} %}
</div>
{% endif %}
<p>{{ footer|raw }}</p>
{% endblock %}
`
engine.RegisterString("template", source)
// Compile the template
tmpl, _ := engine.Load("template")
compiled, _ := tmpl.Compile()
// Serialize using both methods
oldData, _ := oldGobSerialize(compiled)
newData, _ := twig.SerializeCompiledTemplate(compiled)
// Size comparison
fmt.Printf("Old format (gob) size: %d bytes\n", len(oldData))
fmt.Printf("New format (binary) size: %d bytes\n", len(newData))
fmt.Printf("Size reduction: %.2f%%\n\n", (1.0-float64(len(newData))/float64(len(oldData)))*100)
// Benchmark serialization
fmt.Println("Serialization Performance (1000 operations):")
// Old method
iterations := 1000
startOldSer := time.Now()
for i := 0; i < iterations; i++ {
_, _ = oldGobSerialize(compiled)
}
oldSerTime := time.Since(startOldSer)
// New method
startNewSer := time.Now()
for i := 0; i < iterations; i++ {
_, _ = twig.SerializeCompiledTemplate(compiled)
}
newSerTime := time.Since(startNewSer)
fmt.Printf("Old serialization: %v (%.2f µs/op)\n", oldSerTime, float64(oldSerTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("New serialization: %v (%.2f µs/op)\n", newSerTime, float64(newSerTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("Serialization speedup: %.2fx\n\n", float64(oldSerTime.Nanoseconds())/float64(newSerTime.Nanoseconds()))
// Benchmark deserialization
fmt.Println("Deserialization Performance (1000 operations):")
// Old method
startOldDeser := time.Now()
for i := 0; i < iterations; i++ {
_, _ = oldGobDeserialize(oldData)
}
oldDeserTime := time.Since(startOldDeser)
// New method
startNewDeser := time.Now()
for i := 0; i < iterations; i++ {
_, _ = twig.DeserializeCompiledTemplate(newData)
}
newDeserTime := time.Since(startNewDeser)
fmt.Printf("Old deserialization: %v (%.2f µs/op)\n", oldDeserTime, float64(oldDeserTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("New deserialization: %v (%.2f µs/op)\n", newDeserTime, float64(newDeserTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("Deserialization speedup: %.2fx\n\n", float64(oldDeserTime.Nanoseconds())/float64(newDeserTime.Nanoseconds()))
// Total round-trip comparison
fmt.Println("Round-trip Performance (1000 operations):")
// Old method
startOldTotal := time.Now()
for i := 0; i < iterations; i++ {
data, _ := oldGobSerialize(compiled)
_, _ = oldGobDeserialize(data)
}
oldTotalTime := time.Since(startOldTotal)
// New method
startNewTotal := time.Now()
for i := 0; i < iterations; i++ {
data, _ := twig.SerializeCompiledTemplate(compiled)
_, _ = twig.DeserializeCompiledTemplate(data)
}
newTotalTime := time.Since(startNewTotal)
fmt.Printf("Old total: %v (%.2f µs/op)\n", oldTotalTime, float64(oldTotalTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("New total: %v (%.2f µs/op)\n", newTotalTime, float64(newTotalTime.Nanoseconds())/float64(iterations)/1000)
fmt.Printf("Overall speedup: %.2fx\n\n", float64(oldTotalTime.Nanoseconds())/float64(newTotalTime.Nanoseconds()))
// Memory usage estimation
templateCount := 100
fmt.Printf("Memory usage for %d templates:\n", templateCount)
fmt.Printf("Old format: %.2f KB\n", float64(len(oldData)*templateCount)/1024)
fmt.Printf("New format: %.2f KB\n", float64(len(newData)*templateCount)/1024)
fmt.Printf("Memory saved: %.2f KB\n", float64(len(oldData)-len(newData))*float64(templateCount)/1024)
}
// oldGobSerialize simulates the old gob serialization
func oldGobSerialize(compiled *twig.CompiledTemplate) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(compiled); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// oldGobDeserialize simulates the old gob deserialization
func oldGobDeserialize(data []byte) (*twig.CompiledTemplate, error) {
dec := gob.NewDecoder(bytes.NewReader(data))
var compiled twig.CompiledTemplate
if err := dec.Decode(&compiled); err != nil {
return nil, err
}
return &compiled, nil
}