Proposal Details
A common use of time.ParseDuration
is to check whether a string is a valid time.Duration
. However, when the string is not a valid duration, this function can be slow and allocate more memory:
func BenchmarkParseDurationError(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseDuration("9007199254.740993") // missing unit
}
}
/*
BenchmarkParseDurationError-10 19131199 64.33 ns/op 104 B/op 3 allocs/op
BenchmarkParseDurationError-10 18929782 63.66 ns/op 104 B/op 3 allocs/op
BenchmarkParseDurationError-10 19234929 63.45 ns/op 104 B/op 3 allocs/op
*/
As a comparison, a successful parsing operation takes only 20 - 40ns and requires no memory allocation.
This function runs slowly because it extensively uses code like errors.New(“time: invalid duration ” + quote(orig))
to dynamically allocate memory along the error path. In most cases, the code only checks err != nil
and does not use the information within, making these memory allocations unnecessary.
The new ParseDurationError
allows the creation of error message strings to be deferred until its Error
method is called, thereby reducing memory allocation and providing structured error information.
ParseDurationError
could be like:
type ParseDurationError struct {
Message string
Duration string
}
// newParseDurationError creates a new ParseDurationError.
// The provided duration is cloned to avoid escaping.
func newParseDurationError(message, duration string) *ParseDurationError
func (e *ParseDurationError) Error() string
This can make the code nearly 50% faster and use 50% less memory:
BenchmarkParseDurationError-10 40923620 28.11 ns/op 56 B/op 2 allocs/op
BenchmarkParseDurationError-10 42267286 28.22 ns/op 56 B/op 2 allocs/op
BenchmarkParseDurationError-10 42015886 28.33 ns/op 56 B/op 2 allocs/op
Compatibility:
- Error messages format won't be changed. All error messages currently follow a simple and fixed format.
- The actual type of the return value will be altered. Since errors.New
returns an internal type that cannot be directly used in user code, it is unlikely to break existing code.
Comment From: seankhliao
do we need to expose this type? since the caller already has the duration, exposing the type doesn't seem to add much. if we just change the representation of the error, it doesn't need a proposal
Comment From: apocelipes
do we need to expose this type? since the caller already has the duration, exposing the type doesn't seem to add much. if we just change the representation of the error, it doesn't need a proposal
I'm not sure. For me, using internal type parseDurationError
is fine.
Comment From: seankhliao
let's just start with an internal type then. feel free to send a CL
Comment From: gabyhelp
Related Issues
Related Code Changes
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: gopherbot
Change https://go.dev/cl/705195 mentions this issue: time: improve ParseDuration performance for invalid input