mirror of
https://github.com/ii64/sonic.git
synced 2026-06-20 16:45:22 +08:00
feat:(option) add option MaxInlineDepth for addjust compilation inline depth (#287)
* feat: make compilation depth changeable * feat: add option `DefaultMaxInlineDepth` * add recurse depth = 10 * refactor * doc: readme and comment * opt: add `_MAX_FIELDS` to limit the inlining of big struct * update license * fix typo
This commit is contained in:
parent
1a7758c557
commit
94f95f0479
9 changed files with 490 additions and 36 deletions
15
README.md
15
README.md
|
|
@ -284,11 +284,18 @@ import (
|
|||
|
||||
func init() {
|
||||
var v HugeStruct
|
||||
// For most large types (nesting depth <= 5)
|
||||
|
||||
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
|
||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
||||
// If the type is too deep nesting (nesting depth > 5),
|
||||
// you can set compile recursive depth in Pretouch for better stability in JIT.
|
||||
err := sonic.Pretouch(reflect.TypeOf(v), option.WithCompileRecursiveDepth(depth))
|
||||
|
||||
// with more CompileOption...
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
|
||||
// you can set compile recursive loops in Pretouch for better stability in JIT.
|
||||
option.WithCompileRecursiveDepth(loop),
|
||||
// For large nested struct, try to set smaller depth to reduce compiling time.
|
||||
option.WithCompileMaxInlineDepth(depth),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
_MAX_STACK = 5 // cutoff at 5 levels of nesting types
|
||||
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
||||
_MAX_FIELDS = 50 // cutoff at 50 fields struct
|
||||
)
|
||||
|
||||
var _OpNames = [256]string {
|
||||
|
|
@ -487,15 +487,14 @@ type _Compiler struct {
|
|||
|
||||
func newCompiler() *_Compiler {
|
||||
return &_Compiler {
|
||||
opts: option.DefaultCompileOptions(),
|
||||
tab: map[reflect.Type]bool{},
|
||||
rec: map[reflect.Type]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_Compiler) apply(opts option.CompileOptions) *_Compiler {
|
||||
self.opts = opts
|
||||
if self.opts.RecursiveDepth > 0 {
|
||||
self.rec = map[reflect.Type]bool{}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
|
@ -516,15 +515,15 @@ func (self *_Compiler) compile(vt reflect.Type) (ret _Program, err error) {
|
|||
}
|
||||
|
||||
func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type) {
|
||||
ok := self.tab[vt]
|
||||
pt := reflect.PtrTo(vt)
|
||||
|
||||
/* check for recursive nesting */
|
||||
ok := self.tab[vt]
|
||||
if ok {
|
||||
p.rtt(_OP_recurse, vt)
|
||||
return
|
||||
}
|
||||
|
||||
pt := reflect.PtrTo(vt)
|
||||
|
||||
/* check for `json.Unmarshaler` with pointer receiver */
|
||||
if pt.Implements(jsonUnmarshalerType) {
|
||||
p.rtt(_OP_unmarshal_p, pt)
|
||||
|
|
@ -812,7 +811,7 @@ func (self *_Compiler) compileStringBody(p *_Program) {
|
|||
}
|
||||
|
||||
func (self *_Compiler) compileStruct(p *_Program, sp int, vt reflect.Type) {
|
||||
if sp >= _MAX_STACK || p.pc() >= _MAX_ILBUF {
|
||||
if sp >= self.opts.MaxInlineDepth || p.pc() >= _MAX_ILBUF || (sp > 0 && vt.NumField() >= _MAX_FIELDS) {
|
||||
p.rtt(_OP_recurse, vt)
|
||||
if self.opts.RecursiveDepth > 0 {
|
||||
self.rec[vt] = true
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
|||
cfg := option.DefaultCompileOptions()
|
||||
for _, opt := range opts {
|
||||
opt(&cfg)
|
||||
break
|
||||
}
|
||||
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
|
||||
}
|
||||
|
|
@ -189,12 +188,12 @@ func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
|
|||
return nil
|
||||
}
|
||||
next := make(map[reflect.Type]bool)
|
||||
for vt, _ := range(vtm) {
|
||||
for vt := range(vtm) {
|
||||
sub, err := pretouchType(vt, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for svt, _ := range(sub) {
|
||||
for svt := range(sub) {
|
||||
next[svt] = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
_MAX_STACK = 5 // cutoff at 5 levels of nesting types
|
||||
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
||||
_MAX_FIELDS = 50 // cutoff at 50 fields struct
|
||||
)
|
||||
|
||||
var _OpNames = [256]string {
|
||||
|
|
@ -384,7 +384,9 @@ type _Compiler struct {
|
|||
|
||||
func newCompiler() *_Compiler {
|
||||
return &_Compiler {
|
||||
opts: option.DefaultCompileOptions(),
|
||||
tab: map[reflect.Type]bool{},
|
||||
rec: map[reflect.Type]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -658,7 +660,7 @@ func (self *_Compiler) compileString(p *_Program, vt reflect.Type) {
|
|||
}
|
||||
|
||||
func (self *_Compiler) compileStruct(p *_Program, sp int, vt reflect.Type) {
|
||||
if sp >= _MAX_STACK || p.pc() >= _MAX_ILBUF {
|
||||
if sp >= self.opts.MaxInlineDepth || p.pc() >= _MAX_ILBUF || (sp > 0 && vt.NumField() >= _MAX_FIELDS) {
|
||||
p.rtt(_OP_recurse, vt)
|
||||
if self.opts.RecursiveDepth > 0 {
|
||||
self.rec[vt] = true
|
||||
|
|
|
|||
|
|
@ -314,12 +314,12 @@ func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
|
|||
return nil
|
||||
}
|
||||
next := make(map[reflect.Type]bool)
|
||||
for vt, _ := range(vtm) {
|
||||
for vt := range(vtm) {
|
||||
sub, err := pretouchType(vt, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for svt, _ := range(sub) {
|
||||
for svt := range(sub) {
|
||||
next[svt] = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
375
issue_test/pretouch_test.go
Normal file
375
issue_test/pretouch_test.go
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package issue_test
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`compress/gzip`
|
||||
`encoding/json`
|
||||
`io/ioutil`
|
||||
`reflect`
|
||||
`testing`
|
||||
`time`
|
||||
|
||||
. `github.com/bytedance/sonic`
|
||||
`github.com/bytedance/sonic/option`
|
||||
`github.com/stretchr/testify/require`
|
||||
)
|
||||
|
||||
var jsonData = func() string {
|
||||
// Read and decompress the test data.
|
||||
b, err := ioutil.ReadFile("../testdata/synthea_fhir.json.gz")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
zr, err := gzip.NewReader(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(zr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(data)
|
||||
}()
|
||||
|
||||
type (
|
||||
syntheaRoot struct {
|
||||
Entry []struct {
|
||||
FullURL string `json:"fullUrl"`
|
||||
Request *struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
} `json:"request"`
|
||||
Resource *struct {
|
||||
AbatementDateTime time.Time `json:"abatementDateTime"`
|
||||
AchievementStatus syntheaCode `json:"achievementStatus"`
|
||||
Active bool `json:"active"`
|
||||
Activity []struct {
|
||||
Detail *struct {
|
||||
Code syntheaCode `json:"code"`
|
||||
Location syntheaReference `json:"location"`
|
||||
Status string `json:"status"`
|
||||
} `json:"detail"`
|
||||
} `json:"activity"`
|
||||
Address []syntheaAddress `json:"address"`
|
||||
Addresses []syntheaReference `json:"addresses"`
|
||||
AuthoredOn time.Time `json:"authoredOn"`
|
||||
BillablePeriod syntheaRange `json:"billablePeriod"`
|
||||
BirthDate string `json:"birthDate"`
|
||||
CareTeam []struct {
|
||||
Provider syntheaReference `json:"provider"`
|
||||
Reference string `json:"reference"`
|
||||
Role syntheaCode `json:"role"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
} `json:"careTeam"`
|
||||
Category []syntheaCode `json:"category"`
|
||||
Claim syntheaReference `json:"claim"`
|
||||
Class syntheaCoding `json:"class"`
|
||||
ClinicalStatus syntheaCode `json:"clinicalStatus"`
|
||||
Code syntheaCode `json:"code"`
|
||||
Communication []struct {
|
||||
Language syntheaCode `json:"language"`
|
||||
} `json:"communication"`
|
||||
Component []struct {
|
||||
Code syntheaCode `json:"code"`
|
||||
ValueQuantity syntheaCoding `json:"valueQuantity"`
|
||||
} `json:"component"`
|
||||
Contained []struct {
|
||||
Beneficiary syntheaReference `json:"beneficiary"`
|
||||
ID string `json:"id"`
|
||||
Intent string `json:"intent"`
|
||||
Payor []syntheaReference `json:"payor"`
|
||||
Performer []syntheaReference `json:"performer"`
|
||||
Requester syntheaReference `json:"requester"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
Status string `json:"status"`
|
||||
Subject syntheaReference `json:"subject"`
|
||||
Type syntheaCode `json:"type"`
|
||||
} `json:"contained"`
|
||||
Created time.Time `json:"created"`
|
||||
DeceasedDateTime time.Time `json:"deceasedDateTime"`
|
||||
Description syntheaCode `json:"description"`
|
||||
Diagnosis []struct {
|
||||
DiagnosisReference syntheaReference `json:"diagnosisReference"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Type []syntheaCode `json:"type"`
|
||||
} `json:"diagnosis"`
|
||||
DosageInstruction []struct {
|
||||
AsNeededBoolean bool `json:"asNeededBoolean"`
|
||||
DoseAndRate []struct {
|
||||
DoseQuantity *struct {
|
||||
Value float64 `json:"value"`
|
||||
} `json:"doseQuantity"`
|
||||
Type syntheaCode `json:"type"`
|
||||
} `json:"doseAndRate"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Timing *struct {
|
||||
Repeat *struct {
|
||||
Frequency int64 `json:"frequency"`
|
||||
Period float64 `json:"period"`
|
||||
PeriodUnit string `json:"periodUnit"`
|
||||
} `json:"repeat"`
|
||||
} `json:"timing"`
|
||||
} `json:"dosageInstruction"`
|
||||
EffectiveDateTime time.Time `json:"effectiveDateTime"`
|
||||
Encounter syntheaReference `json:"encounter"`
|
||||
Extension []syntheaExtension `json:"extension"`
|
||||
Gender string `json:"gender"`
|
||||
Goal []syntheaReference `json:"goal"`
|
||||
ID string `json:"id"`
|
||||
Identifier []struct {
|
||||
System string `json:"system"`
|
||||
Type syntheaCode `json:"type"`
|
||||
Use string `json:"use"`
|
||||
Value string `json:"value"`
|
||||
} `json:"identifier"`
|
||||
Insurance []struct {
|
||||
Coverage syntheaReference `json:"coverage"`
|
||||
Focal bool `json:"focal"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
} `json:"insurance"`
|
||||
Insurer syntheaReference `json:"insurer"`
|
||||
Intent string `json:"intent"`
|
||||
Issued time.Time `json:"issued"`
|
||||
Item []struct {
|
||||
Adjudication []struct {
|
||||
Amount syntheaCurrency `json:"amount"`
|
||||
Category syntheaCode `json:"category"`
|
||||
} `json:"adjudication"`
|
||||
Category syntheaCode `json:"category"`
|
||||
DiagnosisSequence []int64 `json:"diagnosisSequence"`
|
||||
Encounter []syntheaReference `json:"encounter"`
|
||||
InformationSequence []int64 `json:"informationSequence"`
|
||||
LocationCodeableConcept syntheaCode `json:"locationCodeableConcept"`
|
||||
Net syntheaCurrency `json:"net"`
|
||||
ProcedureSequence []int64 `json:"procedureSequence"`
|
||||
ProductOrService syntheaCode `json:"productOrService"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
ServicedPeriod syntheaRange `json:"servicedPeriod"`
|
||||
} `json:"item"`
|
||||
LifecycleStatus string `json:"lifecycleStatus"`
|
||||
ManagingOrganization []syntheaReference `json:"managingOrganization"`
|
||||
MaritalStatus syntheaCode `json:"maritalStatus"`
|
||||
MedicationCodeableConcept syntheaCode `json:"medicationCodeableConcept"`
|
||||
MultipleBirthBoolean bool `json:"multipleBirthBoolean"`
|
||||
Name json.RawMessage `json:"name"`
|
||||
NumberOfInstances int64 `json:"numberOfInstances"`
|
||||
NumberOfSeries int64 `json:"numberOfSeries"`
|
||||
OccurrenceDateTime time.Time `json:"occurrenceDateTime"`
|
||||
OnsetDateTime time.Time `json:"onsetDateTime"`
|
||||
Outcome string `json:"outcome"`
|
||||
Participant []struct {
|
||||
Individual syntheaReference `json:"individual"`
|
||||
Member syntheaReference `json:"member"`
|
||||
Role []syntheaCode `json:"role"`
|
||||
} `json:"participant"`
|
||||
Patient syntheaReference `json:"patient"`
|
||||
Payment *struct {
|
||||
Amount syntheaCurrency `json:"amount"`
|
||||
} `json:"payment"`
|
||||
PerformedPeriod syntheaRange `json:"performedPeriod"`
|
||||
Period syntheaRange `json:"period"`
|
||||
Prescription syntheaReference `json:"prescription"`
|
||||
PrimarySource bool `json:"primarySource"`
|
||||
Priority syntheaCode `json:"priority"`
|
||||
Procedure []struct {
|
||||
ProcedureReference syntheaReference `json:"procedureReference"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
} `json:"procedure"`
|
||||
Provider syntheaReference `json:"provider"`
|
||||
ReasonCode []syntheaCode `json:"reasonCode"`
|
||||
ReasonReference []syntheaReference `json:"reasonReference"`
|
||||
RecordedDate time.Time `json:"recordedDate"`
|
||||
Referral syntheaReference `json:"referral"`
|
||||
Requester syntheaReference `json:"requester"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
Result []syntheaReference `json:"result"`
|
||||
Series []struct {
|
||||
BodySite syntheaCoding `json:"bodySite"`
|
||||
Instance []struct {
|
||||
Number int64 `json:"number"`
|
||||
SopClass syntheaCoding `json:"sopClass"`
|
||||
Title string `json:"title"`
|
||||
UID string `json:"uid"`
|
||||
} `json:"instance"`
|
||||
Modality syntheaCoding `json:"modality"`
|
||||
Number int64 `json:"number"`
|
||||
NumberOfInstances int64 `json:"numberOfInstances"`
|
||||
Started string `json:"started"`
|
||||
UID string `json:"uid"`
|
||||
} `json:"series"`
|
||||
ServiceProvider syntheaReference `json:"serviceProvider"`
|
||||
Started time.Time `json:"started"`
|
||||
Status string `json:"status"`
|
||||
Subject syntheaReference `json:"subject"`
|
||||
SupportingInfo []struct {
|
||||
Category syntheaCode `json:"category"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
ValueReference syntheaReference `json:"valueReference"`
|
||||
} `json:"supportingInfo"`
|
||||
Telecom []map[string]string `json:"telecom"`
|
||||
Text map[string]string `json:"text"`
|
||||
Total json.RawMessage `json:"total"`
|
||||
Type json.RawMessage `json:"type"`
|
||||
Use string `json:"use"`
|
||||
VaccineCode syntheaCode `json:"vaccineCode"`
|
||||
ValueCodeableConcept syntheaCode `json:"valueCodeableConcept"`
|
||||
ValueQuantity syntheaCoding `json:"valueQuantity"`
|
||||
VerificationStatus syntheaCode `json:"verificationStatus"`
|
||||
} `json:"resource"`
|
||||
} `json:"entry"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
syntheaCode struct {
|
||||
Coding []syntheaCoding `json:"coding"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
syntheaCoding struct {
|
||||
Code string `json:"code"`
|
||||
Display string `json:"display"`
|
||||
System string `json:"system"`
|
||||
Unit string `json:"unit"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
syntheaReference struct {
|
||||
Display string `json:"display"`
|
||||
Reference string `json:"reference"`
|
||||
}
|
||||
syntheaAddress struct {
|
||||
City string `json:"city"`
|
||||
Country string `json:"country"`
|
||||
Extension []syntheaExtension `json:"extension"`
|
||||
Line []string `json:"line"`
|
||||
PostalCode string `json:"postalCode"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
syntheaExtension struct {
|
||||
URL string `json:"url"`
|
||||
ValueAddress syntheaAddress `json:"valueAddress"`
|
||||
ValueCode string `json:"valueCode"`
|
||||
ValueDecimal float64 `json:"valueDecimal"`
|
||||
ValueString string `json:"valueString"`
|
||||
Extension []syntheaExtension `json:"extension"`
|
||||
}
|
||||
syntheaRange struct {
|
||||
End time.Time `json:"end"`
|
||||
Start time.Time `json:"start"`
|
||||
}
|
||||
syntheaCurrency struct {
|
||||
Currency string `json:"currency"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
func TestPretouchSynteaRoot(t *testing.T) {
|
||||
m := new(syntheaRoot)
|
||||
s := time.Now()
|
||||
println("start decoder pretouch:", s.UnixNano())
|
||||
require.Nil(t, Pretouch(reflect.TypeOf(m), option.WithCompileMaxInlineDepth(2), option.WithCompileRecursiveDepth(20)))
|
||||
e := time.Now()
|
||||
println("end decoder pretouch:", e.UnixNano())
|
||||
println("elapsed:", e.Sub(s).Milliseconds(), "ms")
|
||||
|
||||
s = time.Now()
|
||||
println("start decode:", s.UnixNano())
|
||||
require.Nil(t, UnmarshalString(jsonData, m))
|
||||
e = time.Now()
|
||||
println("end decode:", e.UnixNano())
|
||||
d1 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d1, "ns")
|
||||
|
||||
s = time.Now()
|
||||
println("start decode:", s.UnixNano())
|
||||
require.Nil(t, UnmarshalString(jsonData, m))
|
||||
e = time.Now()
|
||||
println("end decode:", e.UnixNano())
|
||||
d2 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d2, "ns")
|
||||
if d1 > d2 * 10 {
|
||||
t.Fatal("decoder pretouch not finish yet")
|
||||
}
|
||||
|
||||
s = time.Now()
|
||||
println("start decode:", s.UnixNano())
|
||||
require.Nil(t, UnmarshalString(jsonData, m))
|
||||
e = time.Now()
|
||||
println("end decode:", e.UnixNano())
|
||||
d5 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d5, "ns")
|
||||
if d2 > d5 * 10 {
|
||||
t.Fatal("decoder pretouch not finish yet")
|
||||
}
|
||||
|
||||
s = time.Now()
|
||||
println("start encode 1:", s.UnixNano())
|
||||
_, err := MarshalString(*m)
|
||||
require.Nil(t, err)
|
||||
e = time.Now()
|
||||
println("end encode 1:", e.UnixNano())
|
||||
d3 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d3, "ns")
|
||||
|
||||
s = time.Now()
|
||||
println("start encode 2:", s.UnixNano())
|
||||
_, err = MarshalString(m)
|
||||
require.Nil(t, err)
|
||||
e = time.Now()
|
||||
println("end encode 2:", e.UnixNano())
|
||||
d4 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d4, "ns")
|
||||
// if d3 > d4 * 10 {
|
||||
// t.Fatal("encoder pretouch not finish yet")
|
||||
// }
|
||||
|
||||
s = time.Now()
|
||||
println("start encode 3:", s.UnixNano())
|
||||
_, err = MarshalString(m)
|
||||
require.Nil(t, err)
|
||||
e = time.Now()
|
||||
println("end encode 3:", e.UnixNano())
|
||||
d6 := e.Sub(s).Nanoseconds()
|
||||
println("elapsed:", d6, "ns")
|
||||
if d4 > d6 * 10 {
|
||||
t.Fatal("encoder pretouch not finish yet")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeSynteaRoot(b *testing.B) {
|
||||
m := new(syntheaRoot)
|
||||
require.Nil(b, Pretouch(reflect.TypeOf(m), option.WithCompileRecursiveDepth(10)))
|
||||
|
||||
b.SetBytes(int64(len(jsonData)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = UnmarshalString(jsonData, m)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeSynteaRoot(b *testing.B) {
|
||||
m := new(syntheaRoot)
|
||||
require.Nil(b, Pretouch(reflect.TypeOf(m), option.WithCompileRecursiveDepth(10)))
|
||||
require.Nil(b, UnmarshalString(jsonData, m))
|
||||
|
||||
b.SetBytes(int64(len(jsonData)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = MarshalString(m)
|
||||
}
|
||||
}
|
||||
27
licenses/LICENSE-golang-asm
Normal file
27
licenses/LICENSE-golang-asm
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -18,28 +18,61 @@ package option
|
|||
|
||||
// CompileOptions includes all options for encoder or decoder compiler.
|
||||
type CompileOptions struct {
|
||||
// the depth for recursive compile
|
||||
// the maximum depth for compilation inline
|
||||
MaxInlineDepth int
|
||||
|
||||
// the loop times for recursively pretouch
|
||||
RecursiveDepth int
|
||||
}
|
||||
|
||||
var (
|
||||
// Default value(3) means the compiler only inline 3 layers of nested struct.
|
||||
// when the depth exceeds, the compiler will recurse
|
||||
// and compile subsequent structs when they are decoded
|
||||
DefaultMaxInlineDepth = 3
|
||||
|
||||
// Default value(1) means `Pretouch()` will be recursively executed once,
|
||||
// if any nested struct is left (depth exceeds MaxInlineDepth)
|
||||
DefaultRecursiveDepth = 1
|
||||
)
|
||||
|
||||
// DefaultCompileOptions set default compile options.
|
||||
func DefaultCompileOptions() CompileOptions {
|
||||
return CompileOptions{
|
||||
RecursiveDepth: 0,
|
||||
RecursiveDepth: DefaultRecursiveDepth,
|
||||
MaxInlineDepth: DefaultMaxInlineDepth,
|
||||
}
|
||||
}
|
||||
|
||||
// CompileOption is a function used to change DefaultCompileOptions.
|
||||
type CompileOption func(o *CompileOptions)
|
||||
|
||||
// WithCompileRecursiveDepth sets the depth of recursive compile
|
||||
// in decoder or encoder.
|
||||
// WithCompileRecursiveDepth sets the loop times of recursive pretouch
|
||||
// in both decoder and encoder,
|
||||
// for both concrete type and its pointer type.
|
||||
//
|
||||
// Default value(0) is suitable for basic types and small nested struct types.
|
||||
//
|
||||
// For large or deep nested struct, try to set larger depth to reduce compile
|
||||
// time in the first Marshal or Unmarshal.
|
||||
func WithCompileRecursiveDepth(depth int) CompileOption {
|
||||
// For deep nested struct (depth exceeds MaxInlineDepth),
|
||||
// try to set more loops to completely compile,
|
||||
// thus reduce JIT unstability in the first hit.
|
||||
func WithCompileRecursiveDepth(loop int) CompileOption {
|
||||
return func(o *CompileOptions) {
|
||||
o.RecursiveDepth = depth
|
||||
if loop < 0 {
|
||||
panic("loop must be >= 0")
|
||||
}
|
||||
o.RecursiveDepth = loop
|
||||
}
|
||||
}
|
||||
|
||||
// WithCompileMaxInlineDepth sets the max depth of inline compile
|
||||
// in decoder and encoder.
|
||||
//
|
||||
// For large nested struct, try to set smaller depth to reduce compiling time.
|
||||
func WithCompileMaxInlineDepth(depth int) CompileOption {
|
||||
return func(o *CompileOptions) {
|
||||
if depth <= 0 {
|
||||
panic("depth must be > 0")
|
||||
}
|
||||
o.MaxInlineDepth = depth
|
||||
}
|
||||
}
|
||||
|
||||
26
sonic.go
26
sonic.go
|
|
@ -159,13 +159,25 @@ func (cfg *frozenConfig) Valid(data []byte) bool {
|
|||
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
|
||||
// a compile option to set the depth of recursive compile for the nested struct type.
|
||||
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
} else if err = decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
// to pretouch the corresponding pointer type as well
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
vt = vt.Elem()
|
||||
} else {
|
||||
vt = reflect.PtrTo(vt)
|
||||
}
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get searches the given path json,
|
||||
|
|
|
|||
Loading…
Reference in a new issue