introduce bytecode caching, update state management to use it

This commit is contained in:
Sky Johnson 2025-07-17 13:48:56 -05:00
parent e86cb55aa6
commit a110b93f5c
2 changed files with 103 additions and 6 deletions

93
state/bytecode.go Normal file
View File

@ -0,0 +1,93 @@
package state
import (
"crypto/sha256"
"fmt"
"sync"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
// BytecodeEntry holds compiled bytecode with metadata
type BytecodeEntry struct {
Bytecode []byte
Name string
Hash [32]byte
}
// Global bytecode cache
var (
bytecodeCache = struct {
sync.RWMutex
entries map[string]*BytecodeEntry
}{
entries: make(map[string]*BytecodeEntry),
}
)
// CompileAndCache compiles code to bytecode and stores it globally
func CompileAndCache(state *luajit.State, code, name string) (*BytecodeEntry, error) {
hash := sha256.Sum256([]byte(code))
cacheKey := fmt.Sprintf("%x", hash)
// Check cache first
bytecodeCache.RLock()
if entry, exists := bytecodeCache.entries[cacheKey]; exists {
bytecodeCache.RUnlock()
return entry, nil
}
bytecodeCache.RUnlock()
// Compile bytecode
bytecode, err := state.CompileBytecode(code, name)
if err != nil {
return nil, err
}
// Store in cache
entry := &BytecodeEntry{
Bytecode: bytecode,
Name: name,
Hash: hash,
}
bytecodeCache.Lock()
bytecodeCache.entries[cacheKey] = entry
bytecodeCache.Unlock()
return entry, nil
}
// GetCached retrieves bytecode from cache by code hash
func GetCached(code string) (*BytecodeEntry, bool) {
hash := sha256.Sum256([]byte(code))
cacheKey := fmt.Sprintf("%x", hash)
bytecodeCache.RLock()
defer bytecodeCache.RUnlock()
entry, exists := bytecodeCache.entries[cacheKey]
return entry, exists
}
// ClearCache removes all cached bytecode entries
func ClearCache() {
bytecodeCache.Lock()
defer bytecodeCache.Unlock()
bytecodeCache.entries = make(map[string]*BytecodeEntry)
}
// CacheStats returns cache statistics
func CacheStats() (int, int64) {
bytecodeCache.RLock()
defer bytecodeCache.RUnlock()
count := len(bytecodeCache.entries)
var totalSize int64
for _, entry := range bytecodeCache.entries {
totalSize += int64(len(entry.Bytecode))
}
return count, totalSize
}

View File

@ -159,15 +159,14 @@ func (s *State) ExecuteFile(scriptPath string) error {
return s.ExecuteString(string(scriptContent), scriptPath)
}
// ExecuteString compiles and runs Lua code
// ExecuteString compiles and runs Lua code with bytecode caching
func (s *State) ExecuteString(code, name string) error {
// Use bytecode compilation for better performance
bytecode, err := s.CompileBytecode(code, name)
entry, err := CompileAndCache(s.State, code, name)
if err != nil {
return fmt.Errorf("compilation error in '%s': %w", name, err)
}
if err := s.LoadAndRunBytecode(bytecode, name); err != nil {
if err := s.LoadAndRunBytecode(entry.Bytecode, name); err != nil {
return fmt.Errorf("execution error in '%s': %w", name, err)
}
@ -188,16 +187,21 @@ func (s *State) ExecuteFileWithResults(scriptPath string) ([]any, error) {
return s.ExecuteStringWithResults(string(scriptContent), scriptPath)
}
// ExecuteStringWithResults executes code and returns all results
// ExecuteStringWithResults executes code and returns all results with bytecode caching
func (s *State) ExecuteStringWithResults(code, name string) ([]any, error) {
baseTop := s.GetTop()
defer s.SetTop(baseTop)
nresults, err := s.Execute(code)
entry, err := CompileAndCache(s.State, code, name)
if err != nil {
return nil, fmt.Errorf("compilation error in '%s': %w", name, err)
}
if err := s.LoadAndRunBytecodeWithResults(entry.Bytecode, name, -1); err != nil {
return nil, fmt.Errorf("execution error in '%s': %w", name, err)
}
nresults := s.GetTop() - baseTop
if nresults == 0 {
return nil, nil
}