这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@foxcpp
Copy link
Collaborator

@foxcpp foxcpp commented Oct 13, 2019

Several preallocations and one clever trick.

Here is comparison with old code using benchmarks added in d569c5e:

benchmark                            old ns/op     new ns/op     delta
BenchmarkTextprotoReadHeader/1-4     16675         12950         -22.34%
BenchmarkTextprotoReadHeader/2-4     21488         16850         -21.58%
BenchmarkTextprotoReadHeader/3-4     36715         31491         -14.23%

benchmark                            old allocs     new allocs     delta
BenchmarkTextprotoReadHeader/1-4     165            106            -35.76%
BenchmarkTextprotoReadHeader/2-4     183            116            -36.61%
BenchmarkTextprotoReadHeader/3-4     359            277            -22.84%

benchmark                            old bytes     new bytes     delta
BenchmarkTextprotoReadHeader/1-4     16494         14249         -13.61%
BenchmarkTextprotoReadHeader/2-4     26270         21622         -17.69%
BenchmarkTextprotoReadHeader/3-4     24978         30808         +23.34%

Header 1 is "the most typical", header 2 contains several very big fields, header 3 contains a lot of small fields.

For reference, here are results using net/textproto parser:

BenchmarkStdlibReadHeader/1-4 	  9779 ns/op	    6224 B/op	      38 allocs/op
BenchmarkStdlibReadHeader/2-4 	  12348 ns/op	   12208 B/op	      41 allocs/op
BenchmarkStdlibReadHeader/3-4 	  21608 ns/op	   12304 B/op	     109 allocs/op

What I think is worth exploring is possibility of deferring trimAroundNewlines to Header.Get/Fields.Value time. This could avoid extra allocation for fields we are not actually interested in. With header fields littered with server tracing information, this can be useful (side note: headers I used in benchmarks are from actual messages l received).

Expect typical header to contain no more than 32 fields.
Expect typical field to be not more than 256 octets long.
@codecov-io
Copy link

codecov-io commented Oct 13, 2019

Codecov Report

Merging #57 into master will decrease coverage by 0.24%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #57      +/-   ##
==========================================
- Coverage   61.32%   61.08%   -0.25%     
==========================================
  Files          16       16              
  Lines         843      848       +5     
==========================================
+ Hits          517      518       +1     
- Misses        280      284       +4     
  Partials       46       46
Impacted Files Coverage Δ
textproto/header.go 77.86% <100%> (+0.7%) ⬆️
textproto/multipart.go 0% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 66913d5...edfb1f0. Read the comment docs.

@foxcpp
Copy link
Collaborator Author

foxcpp commented Oct 13, 2019

Oh, I think it might be a good idea to take a look at how net/textproto makes approximation of fields count in header. Since textproto.ReadHeader is also used for multipart headers, it might be a good idea to not preallocate a lot of fields for them.

Upd: Not sure if it is worth it. It saves around 1 KiB of memory per message at cost of making ReadHeader 20% slower (memory saving is measured using maddy benchmarks, speed is from textproto bench in this PR)

// headerFields.Del for details.
m map[string]*headerField
// Second and further values.
mDups map[string][]*headerField
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'm not sure how I feel about this. Header is already quite complicated, maybe we could extract the logic into MultiStringMap or something? Maybe we could simplify the patch by either using m if there's a single value, either using mDups if there are multiple but avoid having to split values across both?

Maybe we can merge the other commits, and discuss this in a separate PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could simplify the patch by either using m if there's a single value, either using mDups if there are multiple but avoid having to split values across both?

I guess that's probably the better way. I will take a look. For now, I removed that commit from this PR.

Copy link
Owner

@emersion emersion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@emersion emersion merged commit 85cd354 into emersion:master Oct 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants