-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate data with zero allocs #554
Comments
If you call |
AFAICT, my func BenchmarkSetText03(b *testing.B) {
var msg capnp.Message
arena := capnp.SingleSegment(nil)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
seg, err := msg.Reset(arena)
if err != nil {
b.Fatal(err)
}
tx, err := NewTransaction(seg)
if err != nil {
b.Fatal(err)
}
err = tx.SetDescription("my own descr")
if err != nil {
b.Fatal(err)
}
}
// b.Log(arena.String())
} |
I notice that API wise, This is easily solved by instantiating my own arena type that NOPs on Release(). |
Ok, so the first escape to heap is caused because the second time diff --git a/message.go b/message.go
index 5ae7f3e..4bcceaa 100644
--- a/message.go
+++ b/message.go
@@ -100,6 +100,9 @@ func (m *Message) Release() {
func (m *Message) Reset(arena Arena) (first *Segment, err error) {
m.capTable.Reset()
for k := range m.segs {
+ if k == 0 && m.segs[k] == &m.firstSeg {
+ continue
+ }
delete(m.segs, k)
}
@@ -113,6 +116,7 @@ func (m *Message) Reset(arena Arena) (first *Segment, err error) {
DepthLimit: m.DepthLimit,
capTable: m.capTable,
segs: m.segs,
+ firstSeg: Segment{msg: m},
}
if arena != nil { I noticed that If the previous diff is reasonable, I can send it as a proper PR. |
Ok, to fix the second escape to heap I had to create a new Arena that does not use the bufferpool to manage its memory: https://github.com/matheusd/capnptest01/blob/fd2e71a57e5c1ccf8a42f28804ba8f85129002d9/arena.go#L35 This makes it possible to encode messages without any heap allocations (other than the initial buffer if correctly sized). Is |
@matheusd Both of your suggestions seem sensible to me, and a PR is most welcome. Please be aware that I am currently traveling, so I will be slower to respond/review than usual. Looking forward to the PR :) |
(No rush to respond, I'm aware you're travelling) I've sent the first PR to address this (#555). I previously wrote:
But the more I think about it, looking at a cpu profile with the heap allocs fixed, the more I think that In particular, this would allow implementing an arena that forgoes the For example, in the following profile of my benchmark, Rewriting to use an Arena that does not use the map and mutex would make Reset() essentially free. |
Hey @matheusd Thanks for investigating this. Your proposal sounds reasonable. I don't think this will break any existing behavior ... wanna give it a shot? |
Yeah, I've started work on this. I only work on this as time permits, so no hard deadline on when I'll submit, but I think I've got a rough high level design going already. Thank you for the support! |
Absolutely no rush! I'm very, very glad you're able to contribute some cycles 😃 |
How can I generate/encode data with zero heap allocations?
Example use case: I want to generate data to write into a file, so I need to allocate a single structure, message and arena. I can pre-allocate the arena buffer. But all my attempts at writing a loop fail to make it zero alloc (either the arena grows or message escapes to heap).
Here's the set of test benchmarks: https://github.com/matheusd/capnptest01/blob/master/bench_test.go
And here are my results:
While benchmarks 01 and 04 are shown with 0 allocs, that's actually just the amortized growth of the arena which ends up becoming large in those tests.
The text was updated successfully, but these errors were encountered: