Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions benchmark/main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// Corpus benchmark: parse every .php file under the given path and report timing.
// Parsing runs across GOMAXPROCS workers with the GC disabled for the short-lived run.
package main

import (
"fmt"
"io/fs"
"os"
"path/filepath"
"runtime"
"runtime/debug"
"sync"
"sync/atomic"
"time"

"github.com/rectorphp/php-parser-in-go/pkg/conf"
Expand All @@ -26,20 +31,47 @@ func main() {
}
config := conf.Config{Version: phpVersion}

// short-lived process: parse throughput matters, not steady-state memory
debug.SetGCPercent(-1)

files := collectPHPFiles(root)

start := time.Now()
parsed := parseAll(files, config)
elapsed := time.Since(start)

fmt.Printf("parsed %d files in %d ms\n", parsed, elapsed.Milliseconds())
}

// parseAll parses every file across GOMAXPROCS workers and returns the count parsed.
func parseAll(files []string, config conf.Config) int64 {
jobs := make(chan string, runtime.GOMAXPROCS(0))
var parsed int64

var wg sync.WaitGroup
for range runtime.GOMAXPROCS(0) {
wg.Add(1)
go func() {
defer wg.Done()
for path := range jobs {
content, err := os.ReadFile(path)
if err != nil {
continue
}
// broken fixtures return an error; the parse work is what we time
parser.Parse(content, config)
atomic.AddInt64(&parsed, 1)
}
}()
}

for _, path := range files {
content, err := os.ReadFile(path)
if err != nil {
continue
}
// broken fixtures return an error; the parse work is what we time
parser.Parse(content, config)
jobs <- path
}
elapsed := time.Since(start)
close(jobs)
wg.Wait()

fmt.Printf("parsed %d files in %d ms\n", len(files), elapsed.Milliseconds())
return parsed
}

func collectPHPFiles(root string) []string {
Expand Down