Commit 702f9a4
perf: skip joining the int64 parsers' errors for an empty tag (#107)
fillField offers an int64-kinded field's tag to time.ParseDuration and
then to strconv.ParseInt, and since #96, when both reject it, returns
parseErr(fmt.Errorf("%w; %w", err, intErr)). Both reject an empty tag,
and parseErr returns nil for one, so every Set of a zero time.Duration
or other int64-kinded field tagged `default:""` formatted both
messages into a joined error and threw it away. make bench-compare
BASE=v1.10.0 put BenchmarkParse/empty_tag at 143.4 -> 470.1 ns (+228%,
p=0.002) and 80 B / 2 allocs -> 352 B / 10. #96 is not released yet.
The last branch of the int64 case is now `else if defaultVal != ""`,
so the errors are joined only for a tag parseErr reports. An empty tag
falls out of the switch, and fillField returns (false, nil), as
parseErr's nil made it do before. Both parsers still run on it, as the
bool, int, uint and float parsers do, which is v1.10.0's cost. The
check is on the tag as written, so a blank tag, which the duration
attempt trims to nothing, still reports both rejections. The call
stays in parseErr, which reports a type's own unmarshaler rejection in
place of the joined errors (#90), and no unmarshaler is offered an
empty tag, so the check never skips a rejection. The comment on the
int64 case says why the check is there, and BenchmarkParse/empty_tag's
comment no longer says the errors are joined.
Not a behavior change: nothing that succeeded fails, nothing that
failed succeeds, and no message changes. Master's suite passes with
four wrong versions of the check too, so three pins are added for the
int64 branch. Each passes on master's set.go:
- TestSet_DurationEmptyTagReportsNothing: `default:""` on a
time.Duration and an int64 is no error and leaves both zero. Nothing
in make test put an empty tag on an int64-kinded field; only
BenchmarkParse/empty_tag's check did, under make bench-smoke.
- TestSet_WhitespaceTagIsNotEmpty/an_int64_fails_to_parse_it: " " on
an int64 reports both rejections, the duration parser's for "".
- TestSet_FailingUnmarshalerErrorIsReported gains a row for
umFailingDuration, an int64-kinded type whose UnmarshalText always
fails: "1d" reports that rejection alone. No test gave an
int64-kinded type a failing unmarshaler.
go test -race -shuffle=on -v ./... gives 322 PASS lines and no
failures, against 319 on master, and master's set.go with the new
tests gives 322 too. make cover gives 100.0%, make bench-smoke passes
all 45 cases, and gofmt -s -l and go vet are clean. A throwaway probe,
not committed, logs the same on master and on this commit:
`default:""` on a field of every kind, int64-kinded types with failing
unmarshalers among them, is no error and leaves the same values; "1d",
"abc", " ", "\t" and numbers past either end of int64's range give the
same messages, errors.Is matches strconv.ErrSyntax or strconv.ErrRange
as before, and errors.As reaches the *strconv.NumError; and on an
int64-kinded type, a failing unmarshaler's rejection is reported
alone.
Mutation-checked, 5 of 7 killed; master's tests kill 1 of them.
Inverting the check fails 11 tests and subtests, 7 of them on master's
tests. Checking the trimmed tag fails the blank int64 subtest.
Building the error without parseErr fails the int64 unmarshaler row,
and with the check removed as well, the empty-tag test too. Skipping
the error when an unmarshaler rejected the tag fails the row. Two
survive, both the same as this commit in behavior: master's plain
else, which differs in cost only, and adding `|| unmarshalErr != nil`,
since unmarshalErr is always nil for an empty tag.
make bench-compare BASE=origin/master
BENCH='Parse/(empty_tag|int64|duration|kinds)', 6 interleaved rounds
at 400ms, Go 1.26.5 darwin/arm64:
Parse/empty_tag 455.9 ns -> 148.3 ns -67.46% (p=0.002)
352 B / 10 allocs -> 80 B / 2
Parse/int64 179.2 ns, 181.0 ns ~ (p=0.188), 48 B / 3 both
Parse/duration 157.3 ns, 159.4 ns ~ (p=0.058), 16 B / 2 both
Parse/kinds 2.058 µs, 2.051 µs ~ (p=0.457), 128 B / 17 both
The last three give the int64 branch a tag one of its parsers takes,
so none reaches the changed line. Against v1.10.0, with
BENCH='Parse/(unparsed_kind|empty_tag)', empty_tag is 80 B / 2 allocs
on both sides, at 137.0 and 149.0 ns, and unparsed_kind, the cost
every row includes, is 86.74 and 93.93 ns, so most of the time left
over comes from outside the int64 branch.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent 31d8cbc commit 702f9a4
5 files changed
Lines changed: 47 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
190 | | - | |
| 190 | + | |
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
250 | 250 | | |
251 | 251 | | |
252 | 252 | | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
253 | 256 | | |
254 | 257 | | |
255 | 258 | | |
256 | 259 | | |
257 | | - | |
| 260 | + | |
258 | 261 | | |
259 | 262 | | |
260 | 263 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
76 | 92 | | |
77 | 93 | | |
78 | 94 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
278 | 278 | | |
279 | 279 | | |
280 | 280 | | |
281 | | - | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
282 | 284 | | |
283 | 285 | | |
284 | 286 | | |
| |||
290 | 292 | | |
291 | 293 | | |
292 | 294 | | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
293 | 304 | | |
294 | 305 | | |
295 | 306 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
139 | 147 | | |
140 | 148 | | |
141 | 149 | | |
| |||
388 | 396 | | |
389 | 397 | | |
390 | 398 | | |
391 | | - | |
| 399 | + | |
| 400 | + | |
392 | 401 | | |
393 | 402 | | |
394 | 403 | | |
| |||
403 | 412 | | |
404 | 413 | | |
405 | 414 | | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
406 | 418 | | |
407 | 419 | | |
408 | 420 | | |
| |||
412 | 424 | | |
413 | 425 | | |
414 | 426 | | |
| 427 | + | |
415 | 428 | | |
416 | 429 | | |
417 | 430 | | |
| |||
0 commit comments