-
Notifications
You must be signed in to change notification settings - Fork 15
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
implement protoreflect fast paths #8
Conversation
This is how Get looks like: https://github.com/cosmos/cosmos-proto/pull/8/files#diff-b80be7ac49d3c20aabc1048615c419c929d42d171d6337a4f09287f770429697R390 I have some questions on top of my head.
|
Things left to do (for Get):
|
Regarding this, I ran a test: p := &Params{
ConstantFee: &v1alpha1.Coin{
Denom: "non-mutated",
Amount: "1",
},
}
value := p.ProtoReflect().Get(p.ProtoReflect().Descriptor().Fields().ByName("constant_fee"))
value.Message().Set((&v1alpha1.Coin{}).ProtoReflect().Descriptor().Fields().Get(0), protoreflect.ValueOfString("mutated"))
log.Printf("%s", p)
log.Printf("%s", value.Message().Interface()) output:
2021/07/19 10:38:47 constant_fee:{denom:"mutated" amount:"1"}
2021/07/19 10:38:47 denom:"mutated" amount:"1" This makes me assume that fields which are retrieved via If the message type field is not set, a panic occurs in Same behaviour was observed for Map and List. Other behaviour experimentation: Tried to use create a dynamic protobuf message using the func TestDynamic(t *testing.T) {
dynCoin := dynamicpb.NewMessage((&v1alpha1.Coin{}).ProtoReflect().Descriptor())
p := &Params{}
fd := p.ProtoReflect().Descriptor().Fields().ByName("constant_fee")
p.ProtoReflect().Set(fd, protoreflect.ValueOfMessage(dynCoin))
log.Printf("%s", p)
}
panic: invalid type: got *dynamicpb.Message, want *v1alpha1.Coin [recovered]
panic: invalid type: got *dynamicpb.Message, want *v1alpha1.Coin |
i've added the enumeration generators from protoc-gen-go to my branch if you would like to merge that in to your draft |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @fdymylja !
The list struct codegen seems pretty involved. Are they needed? What if we just used something like the dynamicList
from dynamicpb?
testpb/1.pulsar.go
Outdated
// For unpopulated composite types, it returns an empty, read-only view | ||
// of the value; to obtain a mutable reference, use Mutable. | ||
func (x *A) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { | ||
switch descriptor.Name() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not switching on field number? Would that maybe be faster?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we just used something like the dynamicList from dynamicpb?
I've tried combining dynamicpb + normal proto messages and it ends up in invalid type panics -> invalid type: got *dynamicpb.Message, want *v1alpha1.Coin [recovered]
- dynamicpb
also does not give us access to dynamiclist constructors.
we could create a "wrapper", I was experimenting with it but then i got stuck at some point during message.Set
where field is a list field, and value is a protoreflect.ListValue
in which you'd need to cast the value to the slice type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not switching on field number? Would that maybe be faster?
The Get logic will need to be rewritten partially (to also include extension support), because it's not that safe, meaning that using just field name or number I think might not be sufficient, probably the safest way (assuming we're working with a global protobuf registry) is to use the field fullname. Reason being to avoid for example having a field which is number: 1, kind: message
being Get
using a field number: 1, kind: int64
.
The user of Get
would be left with an invalid protoreflect.Value
, and the panic would happen only when value
is being unwrapped.
I ran some tests on original protobuf implementation, If we want to respect protoreflection behaviour: we should use FieldDescriptor.FullName() |
I've update the I've also added the Oneof logic, please have a look at it so we can make sure the logic is correct: https://github.com/cosmos/cosmos-proto/pull/8/files#diff-ec66d96ce4cf4e11416971540fb8c46e759ed99ee0f1cb5e0738bc49172cae1fR255 |
should we have the checkbox list as a separate issue and complete each item in separate PRs? |
went ahead and updated this issue #3 (comment) i think it would be a bit cleaner to track there and do small PR's for each item |
generator/gen.go
Outdated
/* | ||
g.P("// Code generated by Pulsar \U0001FA90. DO NOT EDIT.") | ||
if bi, ok := debug.ReadBuildInfo(); ok { | ||
g.P("// Pulsar version: ", bi.Main.Version) | ||
} | ||
g.P("// source: ", file.Desc.Path()) | ||
g.P() | ||
*/ // TODO(fdymylja): remove me |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should still have the DO NOT EDIT
notice here no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes sorry! I removed it so my editor allows me to see errors or warning! otherwise it won't parse it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove this when I'm done with the PR
sounds perfectly good to me! |
Note on If list is nil || len(list) == 0 -> list cannot be mutated with if there's one element in the list, then the list can be mutated by gettings its protoreflect.ListValue. And the mutation reflects on the message containing the list (so if I append on ListValue then the message containing the list will be updated too).
@aaronc, based on this findings I don't think we can do without custom |
I think it's fine with custom. Was just wondering if we could simplify. Maybe a generic type makes sense, but we should aim for correctness and performance first I think |
I've found a spec discrepancy in
|
I will wrap up this PR early next week, I will also open some issues regarding behaviour in the goproto repo. Then we can focus on testing, if someone is available to write tests I can give a shoot at #5 in the meantime (after I finalize this PR). Would that be fine @aaronc, @technicallyty ? |
All the fastpath implementations are done. I am trying to experiment a little for a faster The generated code might increase a bit but we might get a significant code performance increase. |
Managed to make Range 12x more performant than using standard protoreflect. |
should we just target the main branch here and merge this in now? |
okay ive merged main into the branch, made fastreflect implement the feature interface, and removed all the copied protoc-gen-go code. also i've shortened up I'll merge this in now |
Types to implement:
ProtoReflect.Messsage:
Protoreflect.List:
Protoreflect.Map: