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

773 lines
16 KiB
Go

package luajit_bench
import (
"testing"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
var benchCases = []struct {
name string
code string
}{
{
name: "SimpleAddition",
code: `return 1 + 1`,
},
{
name: "LoopSum",
code: `
local sum = 0
for i = 1, 1000 do
sum = sum + i
end
return sum
`,
},
{
name: "FunctionCall",
code: `
local result = 0
for i = 1, 100 do
result = result + i
end
return result
`,
},
{
name: "TableCreation",
code: `
local t = {}
for i = 1, 100 do
t[i] = i * 2
end
return t[50]
`,
},
{
name: "StringOperations",
code: `
local s = "hello"
for i = 1, 10 do
s = s .. " world"
end
return #s
`,
},
}
func BenchmarkLuaDirectExecution(b *testing.B) {
for _, bc := range benchCases {
b.Run(bc.name, func(b *testing.B) {
L := luajit.New()
if L == nil {
b.Fatal("Failed to create Lua state")
}
defer L.Close()
defer L.Cleanup()
// First verify we can execute the code
if err := L.DoString(bc.code); err != nil {
b.Fatalf("Failed to execute test code: %v", err)
}
b.ResetTimer()
TrackMemoryUsage(b, "direct-"+bc.name, func() {
for i := 0; i < b.N; i++ {
// Execute string and get results
nresults, err := L.Execute(bc.code)
if err != nil {
b.Fatalf("Failed to execute code: %v", err)
}
L.Pop(nresults) // Clean up any results
}
})
})
}
}
func BenchmarkLuaBytecodeExecution(b *testing.B) {
// First compile all bytecode
bytecodes := make(map[string][]byte)
for _, bc := range benchCases {
L := luajit.New()
if L == nil {
b.Fatal("Failed to create Lua state")
}
defer L.Cleanup()
bytecode, err := L.CompileBytecode(bc.code, bc.name)
if err != nil {
L.Close()
b.Fatalf("Error compiling bytecode for %s: %v", bc.name, err)
}
bytecodes[bc.name] = bytecode
L.Close()
}
for _, bc := range benchCases {
b.Run(bc.name, func(b *testing.B) {
L := luajit.New()
if L == nil {
b.Fatal("Failed to create Lua state")
}
defer L.Close()
defer L.Cleanup()
bytecode := bytecodes[bc.name]
// First verify we can execute the bytecode
if err := L.LoadAndRunBytecodeWithResults(bytecode, bc.name, 1); err != nil {
b.Fatalf("Failed to execute test bytecode: %v", err)
}
L.Pop(1) // Clean up the result
b.ResetTimer()
b.SetBytes(int64(len(bytecode))) // Track bytecode size in benchmarks
TrackMemoryUsage(b, "bytecode-"+bc.name, func() {
for i := 0; i < b.N; i++ {
if err := L.LoadAndRunBytecode(bytecode, bc.name); err != nil {
b.Fatalf("Error executing bytecode: %v", err)
}
}
})
})
}
}
// BenchmarkBatchTableOperations compares batch vs individual table operations
func BenchmarkBatchTableOperations(b *testing.B) {
testData := map[string]any{
"name": "testapp",
"version": "1.0.0",
"port": 8080,
"debug": true,
"timeout": 30.5,
"tags": []string{"web", "api", "service"},
"limits": []int{100, 200, 300, 400, 500},
}
b.Run("Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
defer L.Cleanup()
TrackMemoryUsage(b, "table-individual", func() {
for i := 0; i < b.N; i++ {
tb := L.NewTableBuilder()
for k, v := range testData {
switch val := v.(type) {
case string:
tb.SetString(k, val)
case int:
tb.SetNumber(k, float64(val))
case bool:
tb.SetBool(k, val)
case float64:
tb.SetNumber(k, val)
default:
tb.SetTable(k, v)
}
}
tb.Build()
L.Pop(1)
}
})
})
b.Run("Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
defer L.Cleanup()
TrackMemoryUsage(b, "table-batch", func() {
for i := 0; i < b.N; i++ {
tb := L.NewTableBuilder()
tb.BatchBuild(testData)
tb.Build()
L.Pop(1)
}
})
})
b.Run("BatchTableBuilder", func(b *testing.B) {
L := luajit.New()
defer L.Close()
defer L.Cleanup()
TrackMemoryUsage(b, "table-batch-builder", func() {
for i := 0; i < b.N; i++ {
btb := L.NewBatchTableBuilder()
for k, v := range testData {
switch val := v.(type) {
case string:
btb.SetString(k, val)
case int:
btb.SetNumber(k, float64(val))
case bool:
btb.SetBool(k, val)
case float64:
btb.SetNumber(k, val)
default:
btb.SetTable(k, v)
}
}
btb.Build()
L.Pop(1)
}
})
})
}
// BenchmarkBatchArrayOperations compares different array creation methods
func BenchmarkBatchArrayOperations(b *testing.B) {
intArray := make([]int, 50)
stringArray := make([]string, 50)
floatArray := make([]float64, 50)
boolArray := make([]bool, 50)
for i := range 50 {
intArray[i] = i * 2
stringArray[i] = "item" + string(rune('0'+i%10))
floatArray[i] = float64(i) * 1.5
boolArray[i] = i%2 == 0
}
b.Run("IntArray_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "int-array-individual", func() {
for i := 0; i < b.N; i++ {
L.PushValue(intArray)
L.Pop(1)
}
})
})
b.Run("IntArray_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "int-array-batch", func() {
for i := 0; i < b.N; i++ {
L.BatchPushIntArray(intArray)
L.Pop(1)
}
})
})
b.Run("StringArray_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "string-array-individual", func() {
for i := 0; i < b.N; i++ {
L.PushValue(stringArray)
L.Pop(1)
}
})
})
b.Run("StringArray_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "string-array-batch", func() {
for i := 0; i < b.N; i++ {
L.BatchPushStringArray(stringArray)
L.Pop(1)
}
})
})
b.Run("FloatArray_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "float-array-individual", func() {
for i := 0; i < b.N; i++ {
L.PushValue(floatArray)
L.Pop(1)
}
})
})
b.Run("FloatArray_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "float-array-batch", func() {
for i := 0; i < b.N; i++ {
L.BatchPushFloatArray(floatArray)
L.Pop(1)
}
})
})
b.Run("BoolArray_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "bool-array-individual", func() {
for i := 0; i < b.N; i++ {
L.PushValue(boolArray)
L.Pop(1)
}
})
})
b.Run("BoolArray_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "bool-array-batch", func() {
for i := 0; i < b.N; i++ {
L.BatchPushBoolArray(boolArray)
L.Pop(1)
}
})
})
}
// BenchmarkBatchGlobalOperations compares global variable operations
func BenchmarkBatchGlobalOperations(b *testing.B) {
globals := map[string]string{
"APP_NAME": "myapp",
"APP_VERSION": "1.0.0",
"APP_ENV": "production",
"DB_HOST": "localhost",
"DB_PORT": "5432",
"DB_NAME": "mydb",
"CACHE_TTL": "300",
"LOG_LEVEL": "info",
}
b.Run("Individual_Set", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "globals-individual-set", func() {
for i := 0; i < b.N; i++ {
for k, v := range globals {
L.PushString(v)
L.SetGlobal(k)
}
}
})
})
b.Run("Batch_Set", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "globals-batch-set", func() {
for i := 0; i < b.N; i++ {
L.BatchSetGlobals(globals)
}
})
})
// Setup globals for read tests
L := luajit.New()
defer L.Close()
L.BatchSetGlobals(globals)
globalNames := make([]string, 0, len(globals))
for k := range globals {
globalNames = append(globalNames, k)
}
b.Run("Individual_Get", func(b *testing.B) {
TrackMemoryUsage(b, "globals-individual-get", func() {
for i := 0; i < b.N; i++ {
values := make(map[string]any)
for _, name := range globalNames {
L.GetGlobal(name)
if val, err := L.ToValue(-1); err == nil {
values[name] = val
}
L.Pop(1)
}
_ = values
}
})
})
b.Run("Batch_Get", func(b *testing.B) {
TrackMemoryUsage(b, "globals-batch-get", func() {
for i := 0; i < b.N; i++ {
startTop := L.GetTop()
L.BatchGetGlobals(globalNames)
values := make(map[string]any, len(globalNames))
for j, name := range globalNames {
if val, err := L.ToValue(startTop + j + 1); err == nil {
values[name] = val
}
}
L.SetTop(startTop)
_ = values
}
})
})
}
// BenchmarkBatchTableReading compares table field reading methods
func BenchmarkBatchTableReading(b *testing.B) {
L := luajit.New()
defer L.Close()
// Create a complex nested table structure
setupCode := `
config = {
database = {
host = "localhost",
port = 5432,
name = "myapp",
timeout = 30,
ssl = true,
pool = {min = 5, max = 20}
},
server = {
host = "0.0.0.0",
port = 8080,
workers = 4,
timeout = 60,
ssl = false,
middleware = {"cors", "auth", "logging"}
},
cache = {
enabled = true,
ttl = 300,
size = 1000,
backend = "redis",
cluster = false
},
logging = {
level = "info",
file = "/var/log/app.log",
rotate = true,
max_size = 100,
format = "json"
}
}
`
if err := L.DoString(setupCode); err != nil {
b.Fatalf("Setup failed: %v", err)
}
topLevelKeys := []string{"database", "server", "cache", "logging"}
dbKeys := []string{"host", "port", "name", "timeout", "ssl"}
b.Run("Individual_TopLevel", func(b *testing.B) {
TrackMemoryUsage(b, "table-read-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("config")
values := make(map[string]any)
for _, key := range topLevelKeys {
L.GetField(-1, key)
if val, err := L.ToValue(-1); err == nil {
values[key] = val
}
L.Pop(1)
}
L.Pop(1)
_ = values
}
})
})
b.Run("Batch_TopLevel", func(b *testing.B) {
TrackMemoryUsage(b, "table-read-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("config")
reader := L.NewBatchTableReader(-1)
values, _ := reader.ReadFields(topLevelKeys)
L.Pop(1)
_ = values
}
})
})
b.Run("Individual_Nested", func(b *testing.B) {
TrackMemoryUsage(b, "nested-read-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("config")
L.GetField(-1, "database")
values := make(map[string]any)
for _, key := range dbKeys {
L.GetField(-1, key)
if val, err := L.ToValue(-1); err == nil {
values[key] = val
}
L.Pop(1)
}
L.Pop(2)
_ = values
}
})
})
b.Run("Batch_Nested", func(b *testing.B) {
TrackMemoryUsage(b, "nested-read-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("config")
L.GetField(-1, "database")
reader := L.NewBatchTableReader(-1)
values, _ := reader.ReadFields(dbKeys)
L.Pop(2)
_ = values
}
})
})
}
// BenchmarkBatchValuePushing compares value pushing methods
func BenchmarkBatchValuePushing(b *testing.B) {
mixedValues := []any{
"string1", 42, true, 3.14,
"string2", 100, false, 2.718,
"string3", 256, true, 1.414,
"string4", 500, false, 0.577,
}
stringValues := []any{"hello", "world", "test", "bench", "mark", "go", "lua", "jit"}
numberValues := []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b.Run("Mixed_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "mixed-individual", func() {
for i := 0; i < b.N; i++ {
for _, v := range mixedValues {
L.PushValue(v)
}
L.Pop(len(mixedValues))
}
})
})
b.Run("Mixed_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "mixed-batch", func() {
for i := 0; i < b.N; i++ {
pusher := L.NewBatchValuePusher()
for _, v := range mixedValues {
pusher.Add(v)
}
pusher.Push()
L.Pop(len(mixedValues))
}
})
})
b.Run("Strings_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "strings-individual", func() {
for i := 0; i < b.N; i++ {
for _, v := range stringValues {
L.PushValue(v)
}
L.Pop(len(stringValues))
}
})
})
b.Run("Strings_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "strings-batch", func() {
for i := 0; i < b.N; i++ {
pusher := L.NewBatchValuePusher()
for _, v := range stringValues {
pusher.Add(v)
}
pusher.Push()
L.Pop(len(stringValues))
}
})
})
b.Run("Numbers_Individual", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "numbers-individual", func() {
for i := 0; i < b.N; i++ {
for _, v := range numberValues {
L.PushValue(v)
}
L.Pop(len(numberValues))
}
})
})
b.Run("Numbers_Batch", func(b *testing.B) {
L := luajit.New()
defer L.Close()
TrackMemoryUsage(b, "numbers-batch", func() {
for i := 0; i < b.N; i++ {
pusher := L.NewBatchValuePusher()
for _, v := range numberValues {
pusher.Add(v)
}
pusher.Push()
L.Pop(len(numberValues))
}
})
})
}
// BenchmarkBatchExtraction compares array extraction methods
func BenchmarkBatchExtraction(b *testing.B) {
L := luajit.New()
defer L.Close()
// Create test arrays in Lua
setupCode := `
int_array = {}
for i = 1, 100 do
int_array[i] = i * 2
end
float_array = {}
for i = 1, 100 do
float_array[i] = i * 1.5
end
string_array = {}
for i = 1, 100 do
string_array[i] = "item" .. i
end
bool_array = {}
for i = 1, 100 do
bool_array[i] = (i % 2 == 0)
end
`
if err := L.DoString(setupCode); err != nil {
b.Fatalf("Setup failed: %v", err)
}
b.Run("IntArray_Individual", func(b *testing.B) {
TrackMemoryUsage(b, "int-extract-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("int_array")
result := make([]int, 100)
for j := 1; j <= 100; j++ {
L.PushNumber(float64(j))
L.GetTable(-2)
result[j-1] = int(L.ToNumber(-1))
L.Pop(1)
}
L.Pop(1)
_ = result
}
})
})
b.Run("IntArray_Batch", func(b *testing.B) {
TrackMemoryUsage(b, "int-extract-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("int_array")
result, _ := L.BatchExtractIntArray(-1, 100)
L.Pop(1)
_ = result
}
})
})
b.Run("FloatArray_Individual", func(b *testing.B) {
TrackMemoryUsage(b, "float-extract-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("float_array")
result := make([]float64, 100)
for j := 1; j <= 100; j++ {
L.PushNumber(float64(j))
L.GetTable(-2)
result[j-1] = L.ToNumber(-1)
L.Pop(1)
}
L.Pop(1)
_ = result
}
})
})
b.Run("FloatArray_Batch", func(b *testing.B) {
TrackMemoryUsage(b, "float-extract-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("float_array")
result, _ := L.BatchExtractFloatArray(-1, 100)
L.Pop(1)
_ = result
}
})
})
b.Run("StringArray_Individual", func(b *testing.B) {
TrackMemoryUsage(b, "string-extract-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("string_array")
result := make([]string, 100)
for j := 1; j <= 100; j++ {
L.PushNumber(float64(j))
L.GetTable(-2)
result[j-1] = L.ToString(-1)
L.Pop(1)
}
L.Pop(1)
_ = result
}
})
})
b.Run("StringArray_Batch", func(b *testing.B) {
TrackMemoryUsage(b, "string-extract-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("string_array")
result, _ := L.BatchExtractStringArray(-1, 100)
L.Pop(1)
_ = result
}
})
})
b.Run("BoolArray_Individual", func(b *testing.B) {
TrackMemoryUsage(b, "bool-extract-individual", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("bool_array")
result := make([]bool, 100)
for j := 1; j <= 100; j++ {
L.PushNumber(float64(j))
L.GetTable(-2)
result[j-1] = L.ToBoolean(-1)
L.Pop(1)
}
L.Pop(1)
_ = result
}
})
})
b.Run("BoolArray_Batch", func(b *testing.B) {
TrackMemoryUsage(b, "bool-extract-batch", func() {
for i := 0; i < b.N; i++ {
L.GetGlobal("bool_array")
result, _ := L.BatchExtractBoolArray(-1, 100)
L.Pop(1)
_ = result
}
})
})
}