1
0
LuaJIT-to-Go/bench/bench_test.go

786 lines
18 KiB
Go

package luajit_bench
import (
"testing"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
// BenchmarkSimpleDoString benchmarks direct execution of a simple expression
func BenchmarkSimpleDoString(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := "local x = 1 + 1"
b.ResetTimer()
TrackMemoryUsage(b, "dostring", func() {
for i := 0; i < b.N; i++ {
if err := state.DoString(code); err != nil {
b.Fatalf("DoString failed: %v", err)
}
}
})
}
// BenchmarkSimpleCompileAndRun benchmarks compile and run of a simple expression
func BenchmarkSimpleCompileAndRun(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := "local x = 1 + 1"
b.ResetTimer()
TrackMemoryUsage(b, "compile-run", func() {
for i := 0; i < b.N; i++ {
if err := state.CompileAndRun(code, "simple"); err != nil {
b.Fatalf("CompileAndRun failed: %v", err)
}
}
})
}
// BenchmarkSimpleCompileLoadRun benchmarks compile, load, and run of a simple expression
func BenchmarkSimpleCompileLoadRun(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := "local x = 1 + 1"
b.ResetTimer()
TrackMemoryUsage(b, "compile-load-run", func() {
for i := 0; i < b.N; i++ {
bytecode, err := state.CompileBytecode(code, "simple")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
if err := state.LoadAndRunBytecode(bytecode, "simple"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkSimplePrecompiledBytecode benchmarks running precompiled bytecode
func BenchmarkSimplePrecompiledBytecode(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := "local x = 1 + 1"
bytecode, err := state.CompileBytecode(code, "simple")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "precompiled", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadAndRunBytecode(bytecode, "simple"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkFunctionCallDoString benchmarks direct execution of a function call
func BenchmarkFunctionCallDoString(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
// Setup function
setupCode := `
function add(a, b)
return a + b
end
`
if err := state.DoString(setupCode); err != nil {
b.Fatalf("Failed to set up function: %v", err)
}
code := "local result = add(10, 20)"
b.ResetTimer()
TrackMemoryUsage(b, "func-dostring", func() {
for i := 0; i < b.N; i++ {
if err := state.DoString(code); err != nil {
b.Fatalf("DoString failed: %v", err)
}
}
})
}
// BenchmarkFunctionCallPrecompiled benchmarks precompiled function call
func BenchmarkFunctionCallPrecompiled(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
// Setup function
setupCode := `
function add(a, b)
return a + b
end
`
if err := state.DoString(setupCode); err != nil {
b.Fatalf("Failed to set up function: %v", err)
}
code := "local result = add(10, 20)"
bytecode, err := state.CompileBytecode(code, "call")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "func-precompiled", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadAndRunBytecode(bytecode, "call"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkLoopDoString benchmarks direct execution of a loop
func BenchmarkLoopDoString(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
local sum = 0
for i = 1, 1000 do
sum = sum + i
end
`
b.ResetTimer()
TrackMemoryUsage(b, "loop-dostring", func() {
for i := 0; i < b.N; i++ {
if err := state.DoString(code); err != nil {
b.Fatalf("DoString failed: %v", err)
}
}
})
}
// BenchmarkLoopPrecompiled benchmarks precompiled loop execution
func BenchmarkLoopPrecompiled(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
local sum = 0
for i = 1, 1000 do
sum = sum + i
end
`
bytecode, err := state.CompileBytecode(code, "loop")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "loop-precompiled", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadAndRunBytecode(bytecode, "loop"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkTableOperationsDoString benchmarks direct execution of table operations
func BenchmarkTableOperationsDoString(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
local t = {}
for i = 1, 100 do
t[i] = i * 2
end
local sum = 0
for i, v in ipairs(t) do
sum = sum + v
end
`
b.ResetTimer()
TrackMemoryUsage(b, "table-dostring", func() {
for i := 0; i < b.N; i++ {
if err := state.DoString(code); err != nil {
b.Fatalf("DoString failed: %v", err)
}
}
})
}
// BenchmarkTableOperationsPrecompiled benchmarks precompiled table operations
func BenchmarkTableOperationsPrecompiled(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
local t = {}
for i = 1, 100 do
t[i] = i * 2
end
local sum = 0
for i, v in ipairs(t) do
sum = sum + v
end
`
bytecode, err := state.CompileBytecode(code, "table")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "table-precompiled", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadAndRunBytecode(bytecode, "table"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkGoFunctionCall benchmarks calling a Go function from Lua
func BenchmarkGoFunctionCall(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
// Register a simple Go function
add := func(s *luajit.State) int {
a := s.ToNumber(1)
b := s.ToNumber(2)
s.PushNumber(a + b)
return 1
}
if err := state.RegisterGoFunction("add", add); err != nil {
b.Fatalf("RegisterGoFunction failed: %v", err)
}
code := "local result = add(10, 20)"
bytecode, err := state.CompileBytecode(code, "gofunc")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "go-func-call", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadAndRunBytecode(bytecode, "gofunc"); err != nil {
b.Fatalf("LoadAndRunBytecode failed: %v", err)
}
}
})
}
// BenchmarkComplexScript benchmarks a more complex script
func BenchmarkComplexScript(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
-- Define a simple class
local Class = {}
Class.__index = Class
function Class.new(x, y)
local self = setmetatable({}, Class)
self.x = x or 0
self.y = y or 0
return self
end
function Class:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
return self
end
function Class:getPosition()
return self.x, self.y
end
-- Create instances and operate on them
local instances = {}
for i = 1, 50 do
instances[i] = Class.new(i, i*2)
end
local result = 0
for i, obj in ipairs(instances) do
obj:move(i, -i)
local x, y = obj:getPosition()
result = result + x + y
end
return result
`
b.ResetTimer()
TrackMemoryUsage(b, "complex-script", func() {
for i := 0; i < b.N; i++ {
if _, err := state.ExecuteWithResult(code); err != nil {
b.Fatalf("ExecuteWithResult failed: %v", err)
}
}
})
}
// BenchmarkComplexScriptPrecompiled benchmarks a precompiled complex script
func BenchmarkComplexScriptPrecompiled(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
code := `
-- Define a simple class
local Class = {}
Class.__index = Class
function Class.new(x, y)
local self = setmetatable({}, Class)
self.x = x or 0
self.y = y or 0
return self
end
function Class:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
return self
end
function Class:getPosition()
return self.x, self.y
end
-- Create instances and operate on them
local instances = {}
for i = 1, 50 do
instances[i] = Class.new(i, i*2)
end
local result = 0
for i, obj in ipairs(instances) do
obj:move(i, -i)
local x, y = obj:getPosition()
result = result + x + y
end
return result
`
bytecode, err := state.CompileBytecode(code, "complex")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "complex-precompiled", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadBytecode(bytecode, "complex"); err != nil {
b.Fatalf("LoadBytecode failed: %v", err)
}
if err := state.RunBytecodeWithResults(1); err != nil {
b.Fatalf("RunBytecodeWithResults failed: %v", err)
}
state.Pop(1) // Pop the result
}
})
}
// BenchmarkMultipleExecutions benchmarks executing the same bytecode multiple times
func BenchmarkMultipleExecutions(b *testing.B) {
state := luajit.New()
if state == nil {
b.Fatal("Failed to create Lua state")
}
defer state.Close()
// Setup a stateful environment
setupCode := `
counter = 0
function increment(amount)
counter = counter + (amount or 1)
return counter
end
`
if err := state.DoString(setupCode); err != nil {
b.Fatalf("Failed to set up environment: %v", err)
}
// Compile the function call
code := "return increment(5)"
bytecode, err := state.CompileBytecode(code, "increment")
if err != nil {
b.Fatalf("CompileBytecode failed: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "multiple-executions", func() {
for i := 0; i < b.N; i++ {
if err := state.LoadBytecode(bytecode, "increment"); err != nil {
b.Fatalf("LoadBytecode failed: %v", err)
}
if err := state.RunBytecodeWithResults(1); err != nil {
b.Fatalf("RunBytecodeWithResults failed: %v", err)
}
state.Pop(1) // Pop the result
}
})
}
// BenchmarkBatchVsIndividualTableFields compares batch vs individual table field setting
func BenchmarkBatchVsIndividualTableFields(b *testing.B) {
b.Run("Individual", func(b *testing.B) {
state := luajit.New()
defer state.Close()
fields := map[string]string{
"name": "test",
"version": "1.0",
"description": "benchmark",
"author": "user",
"license": "MIT",
"homepage": "example.com",
"repository": "git://example.com",
"keywords": "test,bench",
}
TrackMemoryUsage(b, "individual-fields", func() {
for i := 0; i < b.N; i++ {
tb := state.NewTableBuilder()
for k, v := range fields {
tb.SetString(k, v)
}
tb.Build()
state.Pop(1)
}
})
})
b.Run("Batch", func(b *testing.B) {
state := luajit.New()
defer state.Close()
fields := map[string]string{
"name": "test",
"version": "1.0",
"description": "benchmark",
"author": "user",
"license": "MIT",
"homepage": "example.com",
"repository": "git://example.com",
"keywords": "test,bench",
}
TrackMemoryUsage(b, "batch-fields", func() {
for i := 0; i < b.N; i++ {
btb := state.NewBatchTableBuilder()
for k, v := range fields {
btb.SetString(k, v)
}
btb.Build()
state.Pop(1)
}
})
})
}
// BenchmarkBatchVsIndividualArrays compares batch vs individual array creation
func BenchmarkBatchVsIndividualArrays(b *testing.B) {
intArray := make([]int, 100)
for i := range intArray {
intArray[i] = i * 2
}
b.Run("Individual", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "individual-array", func() {
for i := 0; i < b.N; i++ {
state.CreateTable(len(intArray), 0)
for j, v := range intArray {
state.PushNumber(float64(j + 1))
state.PushNumber(float64(v))
state.SetTable(-3)
}
state.Pop(1)
}
})
})
b.Run("Batch", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "batch-array", func() {
for i := 0; i < b.N; i++ {
state.BatchPushIntArray(intArray)
state.Pop(1)
}
})
})
}
// BenchmarkBatchVsIndividualGlobals compares batch vs individual global variable setting
func BenchmarkBatchVsIndividualGlobals(b *testing.B) {
globals := map[string]string{
"APP_NAME": "myapp",
"APP_VERSION": "1.0.0",
"APP_ENVIRONMENT": "production",
"APP_DEBUG": "false",
"APP_URL": "https://example.com",
"APP_TIMEZONE": "UTC",
"DB_HOST": "localhost",
"DB_PORT": "5432",
}
b.Run("Individual", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "individual-globals", func() {
for i := 0; i < b.N; i++ {
for k, v := range globals {
state.PushString(v)
state.SetGlobal(k)
}
}
})
})
b.Run("Batch", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "batch-globals", func() {
for i := 0; i < b.N; i++ {
state.BatchSetGlobals(globals)
}
})
})
}
// BenchmarkBatchVsIndividualTableReads compares batch vs individual table field reading
func BenchmarkBatchVsIndividualTableReads(b *testing.B) {
state := luajit.New()
defer state.Close()
// Setup test table
setupCode := `
config = {
database = {host = "localhost", port = 5432, name = "mydb"},
server = {host = "0.0.0.0", port = 8080, ssl = true},
logging = {level = "info", file = "/var/log/app.log"},
cache = {enabled = true, ttl = 300, size = 1000}
}
`
if err := state.DoString(setupCode); err != nil {
b.Fatalf("Setup failed: %v", err)
}
keys := []string{"database", "server", "logging", "cache"}
b.Run("Individual", func(b *testing.B) {
TrackMemoryUsage(b, "individual-reads", func() {
for i := 0; i < b.N; i++ {
state.GetGlobal("config")
values := make(map[string]any)
for _, key := range keys {
state.GetField(-1, key)
if val, err := state.ToValue(-1); err == nil {
values[key] = val
}
state.Pop(1)
}
state.Pop(1)
_ = values
}
})
})
b.Run("Batch", func(b *testing.B) {
TrackMemoryUsage(b, "batch-reads", func() {
for i := 0; i < b.N; i++ {
state.GetGlobal("config")
reader := state.NewBatchTableReader(-1)
values, _ := reader.ReadFields(keys)
state.Pop(1)
_ = values
}
})
})
}
// BenchmarkBatchValuePusher compares batch vs individual value pushing
func BenchmarkBatchValuePusher(b *testing.B) {
values := []any{"hello", 42, true, 3.14, "world", 100, false, 2.718}
b.Run("Individual", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "individual-push", func() {
for i := 0; i < b.N; i++ {
for _, v := range values {
state.PushValue(v)
}
state.Pop(len(values))
}
})
})
b.Run("Batch", func(b *testing.B) {
state := luajit.New()
defer state.Close()
TrackMemoryUsage(b, "batch-push", func() {
for i := 0; i < b.N; i++ {
pusher := state.NewBatchValuePusher()
for _, v := range values {
pusher.Add(v)
}
pusher.Push()
state.Pop(len(values))
}
})
})
}
// BenchmarkBatchTypeChecking compares batch vs individual type checking
func BenchmarkBatchTypeChecking(b *testing.B) {
state := luajit.New()
defer state.Close()
// Setup test values on stack
state.PushString("hello")
state.PushNumber(42)
state.PushBoolean(true)
state.PushNil()
checks := []luajit.TypeCheck{
{Index: 1, ExpectedType: luajit.TypeString},
{Index: 2, ExpectedType: luajit.TypeNumber},
{Index: 3, ExpectedType: luajit.TypeBoolean},
{Index: 4, ExpectedType: luajit.TypeNil},
}
b.Run("Individual", func(b *testing.B) {
TrackMemoryUsage(b, "individual-check", func() {
for i := 0; i < b.N; i++ {
for _, check := range checks {
if state.GetType(check.Index) != check.ExpectedType {
b.Fatal("Type mismatch")
}
}
}
})
})
b.Run("Batch", func(b *testing.B) {
TrackMemoryUsage(b, "batch-check", func() {
for i := 0; i < b.N; i++ {
if err := state.BatchCheckTypes(checks); err != nil {
b.Fatal("Type check failed:", err)
}
}
})
})
}
// BenchmarkBatchGlobalManager benchmarks the batch global manager
func BenchmarkBatchGlobalManager(b *testing.B) {
state := luajit.New()
defer state.Close()
setGlobals := map[string]string{
"VAR1": "value1",
"VAR2": "value2",
"VAR3": "value3",
"VAR4": "value4",
}
getGlobals := []string{"VAR1", "VAR2", "VAR3", "VAR4"}
b.Run("Individual", func(b *testing.B) {
TrackMemoryUsage(b, "individual-global-mgmt", func() {
for i := 0; i < b.N; i++ {
// Set globals
for k, v := range setGlobals {
state.PushString(v)
state.SetGlobal(k)
}
// Get globals
results := make(map[string]any)
for _, name := range getGlobals {
state.GetGlobal(name)
if val, err := state.ToValue(-1); err == nil {
results[name] = val
}
state.Pop(1)
}
_ = results
}
})
})
b.Run("Batch", func(b *testing.B) {
TrackMemoryUsage(b, "batch-global-mgmt", func() {
for i := 0; i < b.N; i++ {
mgr := state.NewBatchGlobalManager()
for k, v := range setGlobals {
mgr.QueueSet(k, v)
}
for _, name := range getGlobals {
mgr.QueueGet(name)
}
results, _ := mgr.Execute()
_ = results
}
})
})
}