encoding/asn1: optimize asn1.Unmarshal
Used type-switch instead of switch by reflect.Type and added
BenchmarkUnmarshal.
name old time/op new time/op delta
Marshal-8 28.1µs ± 2% 27.9µs ± 1% ~ (p=0.094 n=9+9)
Unmarshal-8 6.45µs ± 1% 5.83µs ± 4% -9.59% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
Marshal-8 8.26kB ± 0% 8.26kB ± 0% ~ (all equal)
Unmarshal-8 840B ± 0% 488B ± 0% -41.90% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
Marshal-8 363 ± 0% 363 ± 0% ~ (all equal)
Unmarshal-8 50.0 ± 0% 43.0 ± 0% -14.00% (p=0.000 n=10+10)
Change-Id: I6b53833c7a3e2524f025453311841d03c1256a45
GitHub-Pull-Request: golang/go#36341
Reviewed-on: https://go-review.googlesource.com/c/go/+/268557
Trust: Filippo Valsorda <filippo@golang.org>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index 068594e..7c260b4 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -851,53 +851,37 @@
offset += t.length
// We deal with the structures defined in this package first.
- switch fieldType {
- case rawValueType:
- result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]}
- v.Set(reflect.ValueOf(result))
+ switch v := v.Addr().Interface().(type) {
+ case *RawValue:
+ *v = RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]}
return
- case objectIdentifierType:
- newSlice, err1 := parseObjectIdentifier(innerBytes)
- v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
- if err1 == nil {
- reflect.Copy(v, reflect.ValueOf(newSlice))
- }
- err = err1
+ case *ObjectIdentifier:
+ *v, err = parseObjectIdentifier(innerBytes)
return
- case bitStringType:
- bs, err1 := parseBitString(innerBytes)
- if err1 == nil {
- v.Set(reflect.ValueOf(bs))
- }
- err = err1
+ case *BitString:
+ *v, err = parseBitString(innerBytes)
return
- case timeType:
- var time time.Time
- var err1 error
+ case *time.Time:
if universalTag == TagUTCTime {
- time, err1 = parseUTCTime(innerBytes)
- } else {
- time, err1 = parseGeneralizedTime(innerBytes)
+ *v, err = parseUTCTime(innerBytes)
+ return
}
- if err1 == nil {
- v.Set(reflect.ValueOf(time))
- }
- err = err1
+ *v, err = parseGeneralizedTime(innerBytes)
return
- case enumeratedType:
+ case *Enumerated:
parsedInt, err1 := parseInt32(innerBytes)
if err1 == nil {
- v.SetInt(int64(parsedInt))
+ *v = Enumerated(parsedInt)
}
err = err1
return
- case flagType:
- v.SetBool(true)
+ case *Flag:
+ *v = true
return
- case bigIntType:
+ case **big.Int:
parsedInt, err1 := parseBigInt(innerBytes)
if err1 == nil {
- v.Set(reflect.ValueOf(parsedInt))
+ *v = parsedInt
}
err = err1
return
diff --git a/src/encoding/asn1/marshal_test.go b/src/encoding/asn1/marshal_test.go
index 5290522..e3a7d8f 100644
--- a/src/encoding/asn1/marshal_test.go
+++ b/src/encoding/asn1/marshal_test.go
@@ -376,3 +376,31 @@
t.Errorf("Unexpected SET content. got: %s, want: %s", resultSet, expectedOrder)
}
}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ b.ReportAllocs()
+
+ type testCase struct {
+ in []byte
+ out interface{}
+ }
+ var testData []testCase
+ for _, test := range unmarshalTestData {
+ pv := reflect.New(reflect.TypeOf(test.out).Elem())
+ inCopy := make([]byte, len(test.in))
+ copy(inCopy, test.in)
+ outCopy := pv.Interface()
+
+ testData = append(testData, testCase{
+ in: inCopy,
+ out: outCopy,
+ })
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, testCase := range testData {
+ _, _ = Unmarshal(testCase.in, testCase.out)
+ }
+ }
+}