mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
openVerified copied every plugin binary into the instance's private directory before exec, digest or not. The copy is what makes "what was checked" and "what runs" one object, and it stays for a plugin with a Sum. Without one nothing is checked, so the copy kept nothing equal: the bytes at the path when it was taken are the bytes that ran. It cost a full write of the binary on every lazy start (Hanzo's per-app plugins are 24-145 MB), on whatever disk Dir sits on, and gave every instance its own page cache. A plugin with no Sum is now opened read-only and executed through that descriptor. An embedded Bin is written once, under ForkLock (place), and executed from there; before, it was written and then copied. Benchmark_OpenBinary (the file handling alone, Apple M4 Max, APFS, go1.27.1, -count=6, median): size before after 16 MB 3.94 ms 0.026 ms 48 MB 12.11 ms 0.026 ms 144 MB 36.66 ms 0.026 ms Benchmark_ColdStart (Load lazy, first request through the host, test plugin padded to size, same host). On macOS the delta is far larger than the copy itself: exec of a just-written file costs it more than the bytes do. Expect the Linux gain to be the copy above, not this: binary before after 15.8 MB 156.8 ms 7.8 ms 47.8 MB 294.8 ms 7.1 ms 143.8 MB 767.2 ms 7.3 ms Co-authored-by: Hanzo Dev <[email protected]>
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package zip_test
|
|
|
|
// What a lazy plugin's FIRST request costs: start the child from a binary on
|
|
// disk, wait for it to listen, answer. Every request that finds its plugin
|
|
// stopped — the first after boot, the first after IdleAfter reaped it — pays
|
|
// this, so it is the cold tail of every lazily mounted prefix.
|
|
//
|
|
// The binary is the test plugin padded with trailing bytes to the sizes a real
|
|
// fleet ships (Hanzo's per-app plugins run 24-145 MB). Trailing bytes are never
|
|
// mapped, so the process's own startup is the same at every size; what scales
|
|
// with the file is whatever the host does with the file before it execs it.
|
|
//
|
|
// go test -run='^$' -bench=Benchmark_ColdStart -benchtime=40x -count=6 .
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/zap-proto/zip"
|
|
)
|
|
|
|
func Benchmark_ColdStart(b *testing.B) {
|
|
base := buildPluginBin(b, "v1")
|
|
for _, mb := range []int{0, 32, 128} {
|
|
path := padded(b, base, mb)
|
|
b.Run(fmt.Sprintf("pad=%dMB", mb), func(b *testing.B) {
|
|
dir := sockDir(b)
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
host := zip.New(benchConfig())
|
|
host.Use(must(zip.Load(zip.Plugin{Name: "demo", Path: path, Dir: dir, Lazy: true}, "/v1/demo")))
|
|
h := host.Fiber().Handler()
|
|
fctx := getFctx("/v1/demo/version")
|
|
b.StartTimer()
|
|
h(fctx)
|
|
b.StopTimer()
|
|
if code := fctx.Response.StatusCode(); code != 200 {
|
|
b.Fatalf("first request answered %d: %s", code, fctx.Response.Body())
|
|
}
|
|
_ = host.Shutdown()
|
|
b.StartTimer()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// padded copies bin to a file mb MiB longer, beside it.
|
|
func padded(b *testing.B, bin string, mb int) string {
|
|
b.Helper()
|
|
out := filepath.Join(b.TempDir(), fmt.Sprintf("demo-%d", mb))
|
|
src, err := os.Open(bin)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer src.Close()
|
|
dst, err := os.OpenFile(out, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o700)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if _, err := io.Copy(dst, src); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if _, err := dst.Write(make([]byte, mb<<20)); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if err := dst.Close(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
return out
|
|
}
|