aboutsummaryrefslogtreecommitdiff
path: root/src/archive
diff options
context:
space:
mode:
author1911860538 <alxps1911@gmail.com>2025-09-08 16:19:34 +0000
committerGopher Robot <gobot@golang.org>2025-09-08 10:08:20 -0700
commit5e6296f3f8a5fd8c07a0602435eae681002e09ad (patch)
tree5d1420cd1a452856a68e56f360a16e5043e0efb2 /src/archive
parentea00650784bc2909580c7decf729f668349aa939 (diff)
downloadgo-5e6296f3f8a5fd8c07a0602435eae681002e09ad.tar.xz
archive/tar: optimize nanosecond parsing in parsePAXTime
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 <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/archive')
-rw-r--r--src/archive/tar/strconv.go18
-rw-r--r--src/archive/tar/strconv_test.go63
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)
+ }
+ }
+ })
+ }
+}