From f80034ee4fa33245980f2c1e88392e97950faffd Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Fri, 19 Dec 2025 23:06:41 +0100 Subject: [PATCH] benches --- types.go | 13 ++++- types_test.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/types.go b/types.go index 17a2d550..06dd536f 100644 --- a/types.go +++ b/types.go @@ -462,8 +462,12 @@ func extractZvalValue(zval *C.zval, expectedType C.uint8_t) (unsafe.Pointer, err } func zendStringRelease(p unsafe.Pointer) { - zs := (*C.zend_string)(p) - C.zend_string_release(zs) + C.zend_string_release((*C.zend_string)(p)) +} + +// used in tests for cleanup +func zendArrayRelease(p unsafe.Pointer) { + C.zend_array_release((*C.zend_array)(p)) } func zendHashDestroy(p unsafe.Pointer) { @@ -471,6 +475,11 @@ func zendHashDestroy(p unsafe.Pointer) { C.zend_hash_destroy(ht) } +// used in tests for cleanup +func efree(p unsafe.Pointer) { + C.__efree__(p) +} + // EXPERIMENTAL: CallPHPCallable executes a PHP callable with the given parameters. // Returns the result of the callable as a Go interface{}, or nil if the call failed. func CallPHPCallable(cb unsafe.Pointer, params []interface{}) interface{} { diff --git a/types_test.go b/types_test.go index a08f9072..b2b0d551 100644 --- a/types_test.go +++ b/types_test.go @@ -1,6 +1,8 @@ package frankenphp import ( + "fmt" + "io" "log/slog" "testing" @@ -145,3 +147,129 @@ func TestNestedMixedArray(t *testing.T) { assert.Equal(t, originalArray, convertedArray, "nested mixed array should be equal after conversion") }) } + +func benchOnPHPThread(b *testing.B, count int, cb func()) { + globalLogger = slog.New(slog.NewTextHandler(io.Discard, nil)) + _, err := initPHPThreads(1, 1, nil) // boot 1 thread + assert.NoError(b, err) + handler := convertToTaskThread(phpThreads[0]) + + task := newTask(func() { + for i := 0; i < count; i++ { + cb() + } + }) + handler.execute(task) + task.waitForCompletion() + + drainPHPThreads() +} + +func BenchmarkBool(b *testing.B) { + benchOnPHPThread(b, b.N, func() { + phpBool := PHPValue(true) + _, _ = GoValue[bool](phpBool) + efree(phpBool) + }) +} + +func BenchmarkInt(b *testing.B) { + benchOnPHPThread(b, b.N, func() { + phpInt := PHPValue(int64(42)) + _, _ = GoValue[int64](phpInt) + efree(phpInt) + }) +} + +func BenchmarkFloat(b *testing.B) { + benchOnPHPThread(b, b.N, func() { + phpFloat := PHPValue(3.14) + _, _ = GoValue[float64](phpFloat) + efree(phpFloat) + }) +} + +func BenchmarkString(b *testing.B) { + message := "Hello, World!" + benchOnPHPThread(b, b.N, func() { + phpString := PHPString(message, false) + _ = GoString(phpString) + zendStringRelease(phpString) + }) +} + +func BenchmarkInternedString(b *testing.B) { + message := "Error" + benchOnPHPThread(b, b.N, func() { + phpString := PHPString(message, false) + _ = GoString(phpString) + zendStringRelease(phpString) + }) +} + +func BenchmarkEmptyMap(b *testing.B) { + originalMap := map[string]any{} + benchOnPHPThread(b, b.N, func() { + phpArray := PHPMap(originalMap) + _, _ = GoMap[any](phpArray) + zendArrayRelease(phpArray) + }) +} + +func BenchmarkMap5Entries(b *testing.B) { + originalMap := map[string]any{ + "foo1": "bar1", + "foo2": int64(2), + "foo3": true, + "foo4": 3.14, + "foo5": nil, + } + benchOnPHPThread(b, b.N, func() { + phpArray := PHPMap(originalMap) + _, _ = GoMap[any](phpArray) + zendArrayRelease(phpArray) + }) +} + +func BenchmarkAssociativeArray5Entries(b *testing.B) { + originalArray := AssociativeArray[any]{ + Map: map[string]any{ + "foo1": "bar1", + "foo2": int64(2), + "foo3": true, + "foo4": 3.14, + "foo5": nil, + }, + Order: []string{"foo3", "foo1", "foo4", "foo2", "foo5"}, + } + benchOnPHPThread(b, b.N, func() { + phpArray := PHPAssociativeArray(originalArray) + _, _ = GoAssociativeArray[any](phpArray) + zendArrayRelease(phpArray) + }) +} + +func BenchmarkSlice5Entries(b *testing.B) { + originalSlice := []any{"bar1", int64(2), true, 3.14, nil} + benchOnPHPThread(b, b.N, func() { + phpArray := PHPPackedArray(originalSlice) + _, _ = GoPackedArray[any](phpArray) + zendArrayRelease(phpArray) + }) +} + +func BenchmarkMap50Entries(b *testing.B) { + originalMap := map[string]any{} + for i := 0; i < 10; i++ { + originalMap[fmt.Sprintf("foo%d", i*5)] = fmt.Sprintf("val%d", i) + originalMap[fmt.Sprintf("foo%d", i*5+1)] = "Error" // interned string + originalMap[fmt.Sprintf("foo%d", i*5+2)] = true + originalMap[fmt.Sprintf("foo%d", i*5+3)] = 3.12 + originalMap[fmt.Sprintf("foo%d", i*5+4)] = nil + } + benchOnPHPThread(b, b.N, func() { + phpArray := PHPMap(originalMap) + _, _ = GoMap[any](phpArray) + zendArrayRelease(phpArray) + }) +}