mirror of
https://github.com/ii64/sonic.git
synced 2026-06-24 02:16:45 +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() {
|
func init() {
|
||||||
var v HugeStruct
|
var v HugeStruct
|
||||||
// For most large types (nesting depth <= 5)
|
|
||||||
|
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
|
||||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
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.
|
// with more CompileOption...
|
||||||
err := sonic.Pretouch(reflect.TypeOf(v), option.WithCompileRecursiveDepth(depth))
|
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 (
|
const (
|
||||||
_MAX_STACK = 5 // cutoff at 5 levels of nesting types
|
|
||||||
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
||||||
|
_MAX_FIELDS = 50 // cutoff at 50 fields struct
|
||||||
)
|
)
|
||||||
|
|
||||||
var _OpNames = [256]string {
|
var _OpNames = [256]string {
|
||||||
|
|
@ -487,15 +487,14 @@ type _Compiler struct {
|
||||||
|
|
||||||
func newCompiler() *_Compiler {
|
func newCompiler() *_Compiler {
|
||||||
return &_Compiler {
|
return &_Compiler {
|
||||||
|
opts: option.DefaultCompileOptions(),
|
||||||
tab: map[reflect.Type]bool{},
|
tab: map[reflect.Type]bool{},
|
||||||
|
rec: map[reflect.Type]bool{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Compiler) apply(opts option.CompileOptions) *_Compiler {
|
func (self *_Compiler) apply(opts option.CompileOptions) *_Compiler {
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
if self.opts.RecursiveDepth > 0 {
|
|
||||||
self.rec = map[reflect.Type]bool{}
|
|
||||||
}
|
|
||||||
return self
|
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) {
|
func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type) {
|
||||||
ok := self.tab[vt]
|
|
||||||
pt := reflect.PtrTo(vt)
|
|
||||||
|
|
||||||
/* check for recursive nesting */
|
/* check for recursive nesting */
|
||||||
|
ok := self.tab[vt]
|
||||||
if ok {
|
if ok {
|
||||||
p.rtt(_OP_recurse, vt)
|
p.rtt(_OP_recurse, vt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pt := reflect.PtrTo(vt)
|
||||||
|
|
||||||
/* check for `json.Unmarshaler` with pointer receiver */
|
/* check for `json.Unmarshaler` with pointer receiver */
|
||||||
if pt.Implements(jsonUnmarshalerType) {
|
if pt.Implements(jsonUnmarshalerType) {
|
||||||
p.rtt(_OP_unmarshal_p, pt)
|
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) {
|
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)
|
p.rtt(_OP_recurse, vt)
|
||||||
if self.opts.RecursiveDepth > 0 {
|
if self.opts.RecursiveDepth > 0 {
|
||||||
self.rec[vt] = true
|
self.rec[vt] = true
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,6 @@ func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||||
cfg := option.DefaultCompileOptions()
|
cfg := option.DefaultCompileOptions()
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(&cfg)
|
opt(&cfg)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
next := make(map[reflect.Type]bool)
|
next := make(map[reflect.Type]bool)
|
||||||
for vt, _ := range(vtm) {
|
for vt := range(vtm) {
|
||||||
sub, err := pretouchType(vt, opts)
|
sub, err := pretouchType(vt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for svt, _ := range(sub) {
|
for svt := range(sub) {
|
||||||
next[svt] = true
|
next[svt] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_MAX_STACK = 5 // cutoff at 5 levels of nesting types
|
|
||||||
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
||||||
|
_MAX_FIELDS = 50 // cutoff at 50 fields struct
|
||||||
)
|
)
|
||||||
|
|
||||||
var _OpNames = [256]string {
|
var _OpNames = [256]string {
|
||||||
|
|
@ -384,7 +384,9 @@ type _Compiler struct {
|
||||||
|
|
||||||
func newCompiler() *_Compiler {
|
func newCompiler() *_Compiler {
|
||||||
return &_Compiler {
|
return &_Compiler {
|
||||||
|
opts: option.DefaultCompileOptions(),
|
||||||
tab: map[reflect.Type]bool{},
|
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) {
|
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)
|
p.rtt(_OP_recurse, vt)
|
||||||
if self.opts.RecursiveDepth > 0 {
|
if self.opts.RecursiveDepth > 0 {
|
||||||
self.rec[vt] = true
|
self.rec[vt] = true
|
||||||
|
|
|
||||||
|
|
@ -314,12 +314,12 @@ func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
next := make(map[reflect.Type]bool)
|
next := make(map[reflect.Type]bool)
|
||||||
for vt, _ := range(vtm) {
|
for vt := range(vtm) {
|
||||||
sub, err := pretouchType(vt, opts)
|
sub, err := pretouchType(vt, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for svt, _ := range(sub) {
|
for svt := range(sub) {
|
||||||
next[svt] = true
|
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.
|
// CompileOptions includes all options for encoder or decoder compiler.
|
||||||
type CompileOptions struct {
|
type CompileOptions struct {
|
||||||
// the depth for recursive compile
|
// the maximum depth for compilation inline
|
||||||
|
MaxInlineDepth int
|
||||||
|
|
||||||
|
// the loop times for recursively pretouch
|
||||||
RecursiveDepth int
|
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 {
|
func DefaultCompileOptions() CompileOptions {
|
||||||
return CompileOptions{
|
return CompileOptions{
|
||||||
RecursiveDepth: 0,
|
RecursiveDepth: DefaultRecursiveDepth,
|
||||||
|
MaxInlineDepth: DefaultMaxInlineDepth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompileOption is a function used to change DefaultCompileOptions.
|
||||||
type CompileOption func(o *CompileOptions)
|
type CompileOption func(o *CompileOptions)
|
||||||
|
|
||||||
// WithCompileRecursiveDepth sets the depth of recursive compile
|
// WithCompileRecursiveDepth sets the loop times of recursive pretouch
|
||||||
// in decoder or encoder.
|
// 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 deep nested struct (depth exceeds MaxInlineDepth),
|
||||||
//
|
// try to set more loops to completely compile,
|
||||||
// For large or deep nested struct, try to set larger depth to reduce compile
|
// thus reduce JIT unstability in the first hit.
|
||||||
// time in the first Marshal or Unmarshal.
|
func WithCompileRecursiveDepth(loop int) CompileOption {
|
||||||
func WithCompileRecursiveDepth(depth int) CompileOption {
|
|
||||||
return func(o *CompileOptions) {
|
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
|
// 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.
|
// a compile option to set the depth of recursive compile for the nested struct type.
|
||||||
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err = decoder.Pretouch(vt, opts...); err != nil {
|
}
|
||||||
return err
|
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||||
} else {
|
return err
|
||||||
return nil
|
}
|
||||||
}
|
// 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,
|
// Get searches the given path json,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue