From 5e6296f3f8a5fd8c07a0602435eae681002e09ad Mon Sep 17 00:00:00 2001 From: 1911860538 Date: Mon, 8 Sep 2025 16:19:34 +0000 Subject: [PATCH] archive/tar: optimize nanosecond parsing in parsePAXTime MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Modified parsePAXTime to use a byte array for nanosecond parsing, providing a more straightforward implementation with better performance when handling decimal fraction part. Here are benchmark results: goos: darwin goarch: amd64 pkg: archive/tar cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ ParsePAXTIme/NoNanos-8 20.55n ± 4% 20.45n ± 12% ~ (p=1.000 n=10) ParsePAXTIme/ExactNanos-8 52.42n ± 2% 42.16n ± 3% -19.57% (p=0.000 n=10) ParsePAXTIme/WithNanoPadding-8 99.33n ± 2% 39.58n ± 2% -60.16% (p=0.000 n=10) ParsePAXTIme/WithNanoTruncate-8 54.78n ± 1% 43.64n ± 4% -20.34% (p=0.000 n=10) ParsePAXTIme/TrailingError-8 31.87n ± 4% 17.55n ± 2% -44.94% (p=0.000 n=10) ParsePAXTIme/LeadingError-8 31.03n ± 2% 15.81n ± 6% -49.03% (p=0.000 n=10) Change-Id: If05ef512137d0115db9cb6d3ab432335230628bb GitHub-Last-Rev: 106d25e5cfd57e0264b4510c58d09e8f80e13b3f GitHub-Pull-Request: golang/go#73164 Reviewed-on: https://go-review.googlesource.com/c/go/+/662835 Auto-Submit: Michael Pratt LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Pratt Reviewed-by: Dmitri Shuralyov --- src/archive/tar/strconv.go | 18 +++++----- src/archive/tar/strconv_test.go | 63 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go index ac8105efad..217efe9e2e 100644 --- a/src/archive/tar/strconv.go +++ b/src/archive/tar/strconv.go @@ -213,15 +213,17 @@ func parsePAXTime(s string) (time.Time, error) { } // Parse the nanoseconds. - if strings.Trim(sn, "0123456789") != "" { - return time.Time{}, ErrHeader - } - if len(sn) < maxNanoSecondDigits { - sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad - } else { - sn = sn[:maxNanoSecondDigits] // Right truncate + // Initialize an array with '0's to handle right padding automatically. + nanoDigits := [maxNanoSecondDigits]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0'} + for i := range len(sn) { + switch c := sn[i]; { + case c < '0' || c > '9': + return time.Time{}, ErrHeader + case i < len(nanoDigits): + nanoDigits[i] = c + } } - nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + nsecs, _ := strconv.ParseInt(string(nanoDigits[:]), 10, 64) // Must succeed after validation if len(ss) > 0 && ss[0] == '-' { return time.Unix(secs, -1*nsecs), nil // Negative correction } diff --git a/src/archive/tar/strconv_test.go b/src/archive/tar/strconv_test.go index add65e272a..d411153ce2 100644 --- a/src/archive/tar/strconv_test.go +++ b/src/archive/tar/strconv_test.go @@ -439,3 +439,66 @@ func TestFormatPAXRecord(t *testing.T) { } } } + +func BenchmarkParsePAXTIme(b *testing.B) { + tests := []struct { + name string + in string + want time.Time + ok bool + }{ + { + name: "NoNanos", + in: "123456", + want: time.Unix(123456, 0), + ok: true, + }, + { + name: "ExactNanos", + in: "1.123456789", + want: time.Unix(1, 123456789), + ok: true, + }, + { + name: "WithNanoPadding", + in: "1.123", + want: time.Unix(1, 123000000), + ok: true, + }, + { + name: "WithNanoTruncate", + in: "1.123456789123", + want: time.Unix(1, 123456789), + ok: true, + }, + { + name: "TrailingError", + in: "1.123abc", + want: time.Time{}, + ok: false, + }, + { + name: "LeadingError", + in: "1.abc123", + want: time.Time{}, + ok: false, + }, + } + for _, tt := range tests { + b.Run(tt.name, func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ts, err := parsePAXTime(tt.in) + if (err == nil) != tt.ok { + if err != nil { + b.Fatal(err) + } + b.Fatal("expected error") + } + if !ts.Equal(tt.want) { + b.Fatalf("time mismatch: got %v, want %v", ts, tt.want) + } + } + }) + } +} -- 2.52.0