Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve perf on label key validation #52579

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

marcoandredinis
Copy link
Contributor

> go test -benchmem '-run=^$' -bench '^BenchmarkIsValidLabelKey' github.com/gravitational/teleport/api/types/common
goos: darwin
goarch: arm64
pkg: github.com/gravitational/teleport/api/types/common
cpu: Apple M3 Pro
BenchmarkIsValidLabelKeyNew-12          16505014                72.53 ns/op            0 B/op          0 allocs/op
BenchmarkIsValidLabelKeyOld-12           1333614               898.9 ns/op             0 B/op          0 allocs/op
PASS
ok      github.com/gravitational/teleport/api/types/common      2.639s

@marcoandredinis marcoandredinis changed the title Improve Valid Label Key check Improve perf on label key validation Feb 27, 2025
Comment on lines +38 to +57
func isValidLabelKeyByte(b byte) bool {
switch b {
case
// Digits
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

// Lowercase letters
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

// Uppercase letters
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

// Allowed symbols
'/', '.', '_', ':', '*', '-':
return true
default:
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: don't have strong opinion:

Suggested change
func isValidLabelKeyByte(b byte) bool {
switch b {
case
// Digits
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
// Lowercase letters
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
// Uppercase letters
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
// Allowed symbols
'/', '.', '_', ':', '*', '-':
return true
default:
return false
}
func isValidLabelKeyByte(b byte) bool {
return ('0' <= b && b <= '9') || // Digits
('a' <= b && b <= 'z') || // Lowercase letters
('A' <= b && b <= 'Z') || // Uppercase letters
b == '/' || b == '.' || b == '_' || b == ':' || b == '*' || b == '-' // Allowed symbols
}

Copy link
Contributor

Choose a reason for hiding this comment

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

On my cpu this is actually a tiny bit faster (82ns/op vs 89ns/op) but it depends on the data (it becomes worse after adding another test case that has a lot of symbols), I think that the big switch table gives the compiler the best chance to optimize the code now and in the future with better bitwise and numerical tricks (that being said, the chain of conditions and the switch are equivalent so a Sufficiently Smart Compiler should just generate the exact same code for both, but that's basically a pipe dream for Go unfortunately).

I also prefer the aesthetic of the table of letters and numbers. 😌

"label^/.:-LABEL12__34*",
}

for i := 0; i < b.N; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
for i := 0; i < b.N; i++ {
for range b.N {

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of constraining this test file (or a secondary file for the benchmark) to go 1.24 instead of using b.N?

@@ -63,3 +63,19 @@ func TestIsValidLabelKey(t *testing.T) {
})
}
}

func BenchmarkIsValidLabelKey(b *testing.B) {
var labelsForBenchmark = []string{
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
var labelsForBenchmark = []string{
labelsForBenchmark := []string{

Comment on lines +38 to +57
func isValidLabelKeyByte(b byte) bool {
switch b {
case
// Digits
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

// Lowercase letters
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

// Uppercase letters
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

// Allowed symbols
'/', '.', '_', ':', '*', '-':
return true
default:
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

On my cpu this is actually a tiny bit faster (82ns/op vs 89ns/op) but it depends on the data (it becomes worse after adding another test case that has a lot of symbols), I think that the big switch table gives the compiler the best chance to optimize the code now and in the future with better bitwise and numerical tricks (that being said, the chain of conditions and the switch are equivalent so a Sufficiently Smart Compiler should just generate the exact same code for both, but that's basically a pipe dream for Go unfortunately).

I also prefer the aesthetic of the table of letters and numbers. 😌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants