Skip to content

Commit 5e6296f

Browse files
1911860538gopherbot
authored andcommitted
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: 106d25e GitHub-Pull-Request: golang#73164 Reviewed-on: https://go-review.googlesource.com/c/go/+/662835 Auto-Submit: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent ea00650 commit 5e6296f

File tree

2 files changed

+73
-8
lines changed

2 files changed

+73
-8
lines changed

src/archive/tar/strconv.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,17 @@ func parsePAXTime(s string) (time.Time, error) {
213213
}
214214

215215
// Parse the nanoseconds.
216-
if strings.Trim(sn, "0123456789") != "" {
217-
return time.Time{}, ErrHeader
218-
}
219-
if len(sn) < maxNanoSecondDigits {
220-
sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
221-
} else {
222-
sn = sn[:maxNanoSecondDigits] // Right truncate
216+
// Initialize an array with '0's to handle right padding automatically.
217+
nanoDigits := [maxNanoSecondDigits]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0'}
218+
for i := range len(sn) {
219+
switch c := sn[i]; {
220+
case c < '0' || c > '9':
221+
return time.Time{}, ErrHeader
222+
case i < len(nanoDigits):
223+
nanoDigits[i] = c
224+
}
223225
}
224-
nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
226+
nsecs, _ := strconv.ParseInt(string(nanoDigits[:]), 10, 64) // Must succeed after validation
225227
if len(ss) > 0 && ss[0] == '-' {
226228
return time.Unix(secs, -1*nsecs), nil // Negative correction
227229
}

src/archive/tar/strconv_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,66 @@ func TestFormatPAXRecord(t *testing.T) {
439439
}
440440
}
441441
}
442+
443+
func BenchmarkParsePAXTIme(b *testing.B) {
444+
tests := []struct {
445+
name string
446+
in string
447+
want time.Time
448+
ok bool
449+
}{
450+
{
451+
name: "NoNanos",
452+
in: "123456",
453+
want: time.Unix(123456, 0),
454+
ok: true,
455+
},
456+
{
457+
name: "ExactNanos",
458+
in: "1.123456789",
459+
want: time.Unix(1, 123456789),
460+
ok: true,
461+
},
462+
{
463+
name: "WithNanoPadding",
464+
in: "1.123",
465+
want: time.Unix(1, 123000000),
466+
ok: true,
467+
},
468+
{
469+
name: "WithNanoTruncate",
470+
in: "1.123456789123",
471+
want: time.Unix(1, 123456789),
472+
ok: true,
473+
},
474+
{
475+
name: "TrailingError",
476+
in: "1.123abc",
477+
want: time.Time{},
478+
ok: false,
479+
},
480+
{
481+
name: "LeadingError",
482+
in: "1.abc123",
483+
want: time.Time{},
484+
ok: false,
485+
},
486+
}
487+
for _, tt := range tests {
488+
b.Run(tt.name, func(b *testing.B) {
489+
b.ReportAllocs()
490+
for b.Loop() {
491+
ts, err := parsePAXTime(tt.in)
492+
if (err == nil) != tt.ok {
493+
if err != nil {
494+
b.Fatal(err)
495+
}
496+
b.Fatal("expected error")
497+
}
498+
if !ts.Equal(tt.want) {
499+
b.Fatalf("time mismatch: got %v, want %v", ts, tt.want)
500+
}
501+
}
502+
})
503+
}
504+
}

0 commit comments

Comments
 (0)