internal/impl: precompute required bit in validator

Required field validation populates a bitmask of observed required
fields. Store a uint64 containing the bit to set in the validationInfo
rather than the index of the bit. Provides a noticeable speed increase
in validation.

name                             old time/op  new time/op  delta
EmptyMessage/Wire/Unmarshal      40.2ns ± 1%  40.2ns ± 2%    ~     (p=0.860 n=35+37)
EmptyMessage/Wire/Unmarshal-12   7.13ns ± 5%  7.12ns ± 1%    ~     (p=0.112 n=37+37)
RepeatedInt32/Wire/Unmarshal     6.57µs ± 1%  6.46µs ± 1%  -1.56%  (p=0.000 n=39+35)
RepeatedInt32/Wire/Unmarshal-12  1.05µs ± 2%  1.05µs ± 2%    ~     (p=0.659 n=37+33)
Required/Wire/Unmarshal           258ns ± 1%   251ns ± 1%  -2.87%  (p=0.000 n=32+38)
Required/Wire/Unmarshal-12       44.3ns ± 2%  42.4ns ± 1%  -4.36%  (p=0.000 n=36+37)

Change-Id: Ib1cb74d3e348355a6a2f66aecf8fdc4b58cd84d4
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216420
Reviewed-by: Joe Tsai <joetsai@google.com>
This commit is contained in:
Damien Neil 2020-01-24 16:42:42 -08:00
parent ce8f7f6353
commit 170b2bfca6
2 changed files with 13 additions and 10 deletions

View File

@ -143,9 +143,10 @@ func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag wire.Numbe
var o unmarshalOutput
o, err = f.funcs.unmarshal(b, p.Apply(f.offset), wtyp, opts)
n = o.n
if reqi := f.validation.requiredIndex; reqi > 0 && err == nil {
requiredMask |= 1 << (reqi - 1)
if err != nil {
break
}
requiredMask |= f.validation.requiredBit
if f.funcs.isInit != nil && !o.initialized {
initialized = false
}

View File

@ -77,11 +77,13 @@ type validationInfo struct {
typ validationType
keyType, valType validationType
// For non-required fields, requiredIndex is 0.
// For non-required fields, requiredBit is 0.
//
// For required fields, requiredIndex is unique index in the range
// (0, MessageInfo.numRequiredFields].
requiredIndex uint8
// For required fields, requiredBit's nth bit is set, where n is a
// unique index in the range [0, MessageInfo.numRequiredFields).
//
// If there are more than 64 required fields, requiredBit is 0.
requiredBit uint64
}
type validationType uint8
@ -131,7 +133,7 @@ func newFieldValidationInfo(mi *MessageInfo, si structInfo, fd pref.FieldDescrip
// of the required fields past 64.
if mi.numRequiredFields < math.MaxUint8 {
mi.numRequiredFields++
vi.requiredIndex = mi.numRequiredFields
vi.requiredBit = 1 << (mi.numRequiredFields - 1)
}
}
return vi
@ -272,7 +274,7 @@ State:
case 2:
vi.typ = st.valType
vi.mi = st.mi
vi.requiredIndex = 1
vi.requiredBit = 1
}
default:
var f *coderFieldInfo
@ -321,7 +323,7 @@ State:
vi = getExtensionFieldInfo(xt).validation
}
}
if vi.requiredIndex > 0 {
if vi.requiredBit != 0 {
// Check that the field has a compatible wire type.
// We only need to consider non-repeated field types,
// since repeated fields (and maps) can never be required.
@ -337,7 +339,7 @@ State:
ok = wtyp == wire.BytesType
}
if ok {
st.requiredMask |= 1 << (vi.requiredIndex - 1)
st.requiredMask |= vi.requiredBit
}
}
switch vi.typ {