FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

use lazyregexp to compile regexes on first use by thaJeztah · Pull Request #11693 · containerd/containerd · GitHub

use lazyregexp to compile regexes on first use - #11693

Merged
dmcgowan merged 3 commits into
containerd:mainfrom
thaJeztah:lazyregexp
Apr 24, 2025
Merged

use lazyregexp to compile regexes on first use#11693
dmcgowan merged 3 commits into
containerd:mainfrom
thaJeztah:lazyregexp

Conversation

Copy link
Copy Markdown
Member

implement lazyregexp package

Based on the "lazyregexp" package in golang.org/x/mod;
https://cs.opensource.google/go/x/mod/+/refs/tags/v0.19.0:internal/lazyregexp/lazyre.go;l=66-78

This package allows defining regular expressions that should not be
compiled until used, but still providing validation to prevent
invalid regular expressions from producing a panic at runtime.

The lazyregexp package provides a subset of the methods provided
by "regexp" and only implements the methods used in the codebase.
Additional methods can be added when needed.

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

Comment thread .golangci.yml

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Changes in this file will conflict with another PR currently pending;

Copy link
Copy Markdown
Member

Does this have a significant benefit?
Do you have a benchmark result?

Copy link
Copy Markdown
Member Author

I don't have benchmarks for this case (and it may depend where these packages are used); I know both @kolyshkin and @tonistiigi have looked at use of regexp.MustCompile in library code in various projects, as they tended to bring additional overhead even when not used; some related discussions with some examples;

Copy link
Copy Markdown
Member Author

@AkihiroSuda OK; did some rudimentary comparisons based on just the github.com/containerd/containerd/v2 packages (I didn't compare (clock) time, as it fluctuated too much);

  • ctr; -16576 bytes (-9.85%), -202 allocations (-34.47%)
  • containerd; -18616 bytes (-10.98%), -154 allocations (-6.15%)
  • containerd-shim-runc-v2; -5040 bytes (-77.68%), -70 allocations (-76.92%)

For those, I ran (e.g.);

Before:

GODEBUG=inittrace=1 containerd-shim-runc-v2 --version 2>&1 | grep github.com/containerd/containerd/v2
init github.com/containerd/containerd/v2/version @0.48 ms, 0.043 ms clock, 0 bytes, 0 allocs
init github.com/containerd/containerd/v2/pkg/identifiers @3.6 ms, 0.063 ms clock, 5184 bytes, 73 allocs
init github.com/containerd/containerd/v2/pkg/protobuf @4.2 ms, 0.007 ms clock, 128 bytes, 3 allocs
init github.com/containerd/containerd/v2/core/mount @4.7 ms, 0.001 ms clock, 0 bytes, 0 allocs
init github.com/containerd/containerd/v2/pkg/sys/reaper @4.7 ms, 0 ms clock, 48 bytes, 1 allocs
init github.com/containerd/containerd/v2/core/runtime @4.8 ms, 0.007 ms clock, 992 bytes, 11 allocs
init github.com/containerd/containerd/v2/cmd/containerd-shim-runc-v2/task/plugin @4.8 ms, 0.001 ms clock, 136 bytes, 3 allocs

After:

GODEBUG=inittrace=1 containerd-shim-runc-v2 --version 2>&1 | grep github.com/containerd/containerd/v2
init github.com/containerd/containerd/v2/version @0.30 ms, 0.004 ms clock, 0 bytes, 0 allocs
init github.com/containerd/containerd/v2/internal/lazyregexp @2.1 ms, 0 ms clock, 0 bytes, 0 allocs
init github.com/containerd/containerd/v2/pkg/identifiers @2.1 ms, 0.001 ms clock, 144 bytes, 3 allocs
init github.com/containerd/containerd/v2/pkg/protobuf @2.8 ms, 0.006 ms clock, 128 bytes, 3 allocs
init github.com/containerd/containerd/v2/core/mount @3.3 ms, 0.001 ms clock, 0 bytes, 0 allocs
init github.com/containerd/containerd/v2/pkg/sys/reaper @3.3 ms, 0 ms clock, 48 bytes, 1 allocs
init github.com/containerd/containerd/v2/core/runtime @3.4 ms, 0.006 ms clock, 992 bytes, 11 allocs
init github.com/containerd/containerd/v2/cmd/containerd-shim-runc-v2/task/plugin @3.5 ms, 0.001 ms clock, 136 bytes, 3 allocs

djdongjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

minor comments, LGTM overall

Comment thread internal/cri/bandwidth/linux.go Outdated
Comment thread internal/lazyregexp/lazyregexp.go Outdated

Copy link
Copy Markdown
Contributor

It might make sense to drop the use of regexp altogether at least in some cases.

Regexp is good to have when the pattern can be different (for example, supplied from config). It can be useful when the parsing logic is very complex.

In other cases (relatively simple pattern which won't change) it's better to reimplement the logic.

For example, here is Validate from pkg/indentifiers which don't use regexp, mostly written by Claude. It's faster, cleaner, and the logic is easy to follow:

func Validate(s string) error {
	if len(s) == 0 {
		return fmt.Errorf("identifier must not be empty: %w", errdefs.ErrInvalidArgument)
	}

	if len(s) > maxLength {
		return fmt.Errorf("identifier %q greater than maximum length (%d characters): %w", s, maxLength, errdefs.ErrInvalidArgument)
	}

	// Must start with alphanumeric character.
	if !isAlphaNum(s[0]) {
		return fmt.Errorf("identifier %q must begin with alphanumeric character: %w", s, errdefs.ErrInvalidArgument)
	}

	// Now check the pattern: alphanumeric followed by optional (separator + alphanumeric).
	i := 0
	// Parse first alphanumeric segment.
	for i < len(s) && isAlphaNum(s[i]) {
		i++
	}

	// Now we should have alternating separator and alphanumeric segments.
	for i < len(s) {
		// Must be a separator.
		if !isSep(s[i]) {
			return fmt.Errorf("identifier %q must match pattern of alphanumeric segments separated by '.', '_', or '-': %w", s, errdefs.ErrInvalidArgument)
		}
		i++

		// Separator must be followed by at least one alphanumeric.
		if i >= len(s) || !isAlphaNum(s[i]) {
			return fmt.Errorf("identifier %q must have alphanumeric characters after each separator: %w", s, errdefs.ErrInvalidArgument)
		}

		// Parse alphanumeric segment.
		for i < len(s) && isAlphaNum(s[i]) {
			i++
		}
	}

	return nil
}

func isAlphaNum(c byte) bool {
	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
}

func isSep(c byte) bool {
	return c == '.' || c == '_' || c == '-'
}

Copy link
Copy Markdown
Member

Can you rebase to pick up the CI fixes

Copy link
Copy Markdown
Member Author

rebased 👍

@kolyshkin thanks! Yes, I think rewriting some of these could make sense. I did this one as I had done this approach in some of the other repositories, and it at least addresses the immediate issue, but yeah, sometimes regexes have been used a bit too early. I might spend some time on those on a rainy afternoon

Copy link
Copy Markdown
Member

Looks good, can you squash the package updates into a single commit

Based on the "lazyregexp" package in golang.org/x/mod;
https://cs.opensource.google/go/x/mod/+/refs/tags/v0.19.0:internal/lazyregexp/lazyre.go;l=66-78

This package allows defining regular expressions that should not be
compiled until used, but still providing validation to prevent
invalid regular expressions from producing a panic at runtime.

The lazyregexp package provides a subset of the methods provided
by "regexp" and only implements the methods used in the codebase.
Additional methods can be added when needed.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
- internal/cri/bandwidth: use lazyregexp to compile regexes on first use
- pkg/identifiers: use lazyregexp to compile regexes on first use
- pkg/progress: use lazyregexp to compile regexes on first use
- pkg/reference: use lazyregexp to compile regexes on first use
- pkg/sys: use lazyregexp to compile regexes on first use

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Copy link
Copy Markdown
Member Author

done; let me know if this is what you meant

Copy link
Copy Markdown
Member

/retest

github-project-automation Bot moved this from Needs Triage to Review In Progress in Pull Request Review Apr 23, 2025

Copy link
Copy Markdown
Member Author

all green again 👍

estesp added this pull request to the merge queue Apr 23, 2025
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2025
dmcgowan added this pull request to the merge queue Apr 23, 2025
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2025
dmcgowan added this pull request to the merge queue Apr 23, 2025
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 23, 2025
dmcgowan added this pull request to the merge queue Apr 24, 2025
dmcgowan moved this from Review In Progress to Merge on Green in Pull Request Review Apr 24, 2025
Merged via the queue into containerd:main with commit fad6366 Apr 24, 2025
github-project-automation Bot moved this from Merge on Green to Done in Pull Request Review Apr 24, 2025
thaJeztah deleted the lazyregexp branch April 24, 2025 05:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

8 participants


Back | FazBrowse Home | New Git URL