From 6f6f9665805340171e6ad20cf55d62e7b4b102fb Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 26 Nov 2025 23:12:10 -0700 Subject: [PATCH 1/3] Add benchmark for register and graph Signed-off-by: Derek McGowan --- plugin_test.go | 61 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/plugin_test.go b/plugin_test.go index 185eda5..0c52f6c 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -26,27 +26,27 @@ func mockPluginFilter(*Registration) bool { return false } -// TestContainerdPlugin tests the logic of Graph, use the containerd's plugin -func TestContainerdPlugin(t *testing.T) { - // Plugin types commonly used by containerd - const ( - InternalPlugin Type = "io.containerd.internal.v1" - RuntimePlugin Type = "io.containerd.runtime.v1" - RuntimePluginV2 Type = "io.containerd.runtime.v2" - ServicePlugin Type = "io.containerd.service.v1" - GRPCPlugin Type = "io.containerd.grpc.v1" - SnapshotPlugin Type = "io.containerd.snapshotter.v1" - TaskMonitorPlugin Type = "io.containerd.monitor.v1" - DiffPlugin Type = "io.containerd.differ.v1" - MetadataPlugin Type = "io.containerd.metadata.v1" - ContentPlugin Type = "io.containerd.content.v1" - GCPlugin Type = "io.containerd.gc.v1" - LeasePlugin Type = "io.containerd.lease.v1" - TracingProcessorPlugin Type = "io.containerd.tracing.processor.v1" - ) +// Plugin types commonly used by containerd +const ( + InternalPlugin Type = "io.containerd.internal.v1" + RuntimePlugin Type = "io.containerd.runtime.v1" + RuntimePluginV2 Type = "io.containerd.runtime.v2" + ServicePlugin Type = "io.containerd.service.v1" + GRPCPlugin Type = "io.containerd.grpc.v1" + SnapshotPlugin Type = "io.containerd.snapshotter.v1" + TaskMonitorPlugin Type = "io.containerd.monitor.v1" + DiffPlugin Type = "io.containerd.differ.v1" + MetadataPlugin Type = "io.containerd.metadata.v1" + ContentPlugin Type = "io.containerd.content.v1" + GCPlugin Type = "io.containerd.gc.v1" + LeasePlugin Type = "io.containerd.lease.v1" + TracingProcessorPlugin Type = "io.containerd.tracing.processor.v1" +) + +func testRegistry() Registry { var register Registry - register = register.Register(&Registration{ + return register.Register(&Registration{ Type: TaskMonitorPlugin, ID: "cgroups", }).Register(&Registration{ @@ -223,7 +223,11 @@ func TestContainerdPlugin(t *testing.T) { TracingProcessorPlugin, }, }) +} +// TestContainerdPlugin tests the logic of Graph, use the containerd's plugin +func TestContainerdPlugin(t *testing.T) { + register := testRegistry() ordered := register.Graph(mockPluginFilter) expectedURI := []string{ "io.containerd.monitor.v1.cgroups", @@ -512,3 +516,22 @@ func testPlugin(t Type, id string, i interface{}, err error) *Plugin { err: err, } } + +func BenchmarkGraph(b *testing.B) { + register := testRegistry() + b.ResetTimer() + for i := 0; i < b.N; i++ { + register.Graph(mockPluginFilter) + } +} + +func BenchmarkUnique(b *testing.B) { + register := testRegistry() + b.ResetTimer() + for i := 0; i < b.N; i++ { + checkUnique(register, &Registration{ + Type: InternalPlugin, + ID: "new", + }) + } +} From c9ccfc6ec0456718e3d0ae3a3c93258cfa89aa27 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 26 Nov 2025 23:50:30 -0700 Subject: [PATCH 2/3] Fix comparison of children in graph generation Replace the URI comparison with pointer comparison, this removes all allocations during the children comparison. The register already guarantees that multiple URIs are not registered. Change the ordering of the comparison first check the most likely filter and do the set check last. Signed-off-by: Derek McGowan --- plugin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.go b/plugin.go index f7899e1..7fb8f2a 100644 --- a/plugin.go +++ b/plugin.go @@ -135,7 +135,7 @@ func (registry Registry) Graph(filter DisableFilter) []Registration { func children(reg *Registration, registry []*Registration, added, disabled map[*Registration]bool, ordered *[]Registration) { for _, t := range reg.Requires { for _, r := range registry { - if !disabled[r] && r.URI() != reg.URI() && (t == "*" || r.Type == t) { + if (t == "*" || r.Type == t) && r != reg && !disabled[r] { children(r, registry, added, disabled, ordered) if !added[r] { *ordered = append(*ordered, *r) From 60d3c68c007185965c1d97f01cb3b82c7dee548d Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Thu, 27 Nov 2025 00:10:14 -0700 Subject: [PATCH 3/3] Avoid generating URI when checking unique The URI generation is slow and unnecessary just to compare two fields. This reduces extra allocations down to 0 during registration. Signed-off-by: Derek McGowan --- plugin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.go b/plugin.go index 7fb8f2a..1196e0f 100644 --- a/plugin.go +++ b/plugin.go @@ -170,7 +170,7 @@ func (registry Registry) Register(r *Registration) Registry { func checkUnique(registry Registry, r *Registration) error { for _, registered := range registry { - if r.URI() == registered.URI() { + if r.Type == registered.Type && r.ID == registered.ID { return fmt.Errorf("%s: %w", r.URI(), ErrIDRegistered) } }