-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Go speed testing #165
Go speed testing #165
Conversation
Identifies alloc-heavy codepaths.
…ffers into go-faster
Change the signature for 'string' getters and settings to use byte slices instead of strings.
Builder has a new CreateByteString function that writes a null-terimnated byte slice to the buffer. This results in zero allocations for writing strings.
Add the function `Reset` to the Builder, which facilitates reuse of the underlying byte slice.
So, do you want to change this so it generates accessors for both strings and byte vectors? Or do you think Go programmers will be happy with just the the byte vectors? Also, if you can squash related commits, that's helpful in code review. |
The problem with generating multiple accessors is that we can't guarantee they won't collide with other schema-defined fields. For example, a field called 'Name' would get 'Name' and 'NameBytes' as accessors. If there is a 'NameBytes' field (say, a [uchar]), then we'd get a collision. Personally, I'd rather let the user make the choice whether to allocate a string, at the cost of some syntax noise. That means returning byte slices in all cases. Anyone else want to share an opinion? |
I'm fine either way, to me it really depends on how people typically use Go. As far as collisions go, we already have this problem with "Type" and "Length" suffixes (depending on language), which is currently handled in an ad-hoc way in the parser by giving an error for any fields defined that would clash (see |
|
Reduced number of backing buffer growth events, bringing |
I'm hearing that users want to be able to provide their own backing buffer, to have more control over memory usage, and potentially reduce allocs. Something like NewBuilderFromSlice(...) or bldr:= &Builder{Bytes: myslice}
bldr.Reset() // ensures correct initial state of the byte slice is what is being asked for. This approach isn't as simple as it seems, though, because a Go (Note that to set any of these values directly would require making them publicly visible. I'd rather not make So, if users want this, we have a few paths.
myCfg := BuilderConfig{
InitialBytesBuffer: mySlice1,
InitialVTableBuffer: mySlice2,
InitialVTablesBuffer: mySlice3,
}
bldr := NewBuilder(&myCfg) (Simple use cases will just pass
bldr := &Builder{Bytes: mySlice, VTables: mySlice2}
bldr.Reset() // Sets up bookkeeping I'm not a fan of this because, like I said, we'd have to make the internal state fields public so that users could set them. Feedback? |
@rw how hard would it be for the builder to take just one buffer and segment it as needed internally so that implementation details are hidden from client code? If any one of the segments (bytes, vtable, vtables, etc.) needed to grow, allocate a whole new buffer, copy each of the segments into the new buffer, and continue. |
@dgnorton That's an interesting idea. As a caller, that's simpler (but less flexible) than using a @gwvo Any thoughts? |
In C++ we provide an allocator call-back, is there something similar that can be done in Go? |
An allocator callback in Go would be something like this: bldr := NewBuilder(myAllocator)
func myAllocator(existingSlice []byte, desiredCapacity int) []byte {
// Look in a sync.Pool or somewhere else for a suitable slice.
// Or make a new one on the heap:
if existingSlice == nil {
return make([]byte, desiredCapacity)
}
existingSlice = existingSlice[:cap(existingSlice)]
extension := make([]byte, desiredCapacity - cap(existingSlice))
extended := append(existingSlice, extension...)
return extended
} (This was edited.) This has the added advantage that we could use it for growing the byte buffer, too. @gwvo Is this the kind of thing you mean? @dgnorton Would you use this? |
(Keep in mind that at this point, you could just have a pool of |
Yes.. though reusing FlatBufferBuilder objects seems preferable in most situations. |
@rw I'm not sure a pool of builders would work. E.g., in our case...
We have a problem at that last step because the builder still owns the memory that we passed down the pipe. We could pass the builder down the pipe but that seems awkward. Maybe I'm thinking about it wrong and there's a better way? |
I'd like to handle a public interface for allocations in a separate feature branch. Let's get these speed improvements shipped. A workaround (for those who want it) is to manually manage the publicly-accessible |
…eted_script delete obsoleted scripts
No description provided.