-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: reflect: add NewAtPtr, ValueAt #41993
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
Comments
the double loop in the benchmark looks weird, also it violates the pointer safety rules |
Accepted it and fixed. |
What kind of code would use these new functions? |
For example, It allows more fast access to the struct fields by pre-cached offsets. src := reflect.ValueAt(srcFieldType, unsafe.Pointer(uintptr(srcPtr)+srcOffset))
dest := reflect.ValueAt(destFieldType, unsafe.Pointer(uintptr(destPtr)+destOffset))
dest.Set(src) |
Have you attempted to make the current APIs faster? Also, if you find reflect to be too slow for your use case in general, you could always give up some safety and use more unsafe directly instead of reflect. You appear to be using unsafe pointer arithmetic already. |
I don't see way to do it.
Go does't have a function that allows to copy arbitrary count of bytes. If some memory copy function were there, it would most likely solve the problem. And one way, that i know, to copy the variable of unknown type is to use Set(), that is required the indirect Value and which can only be obtained in the following way: src := reflect.NewAt(srcType, srcPtr).Elem() // slow
dest := reflect.NewAt(destType, destPtr).Elem() // slow
dest.Set(src) |
With the copy function and a few unsafe casts to array of byte, you can actually copy an arbitrary amount of bytes. |
The only difference between this API and the existing API is which type gets passed in. If T is constructed at runtime, the data is writable, so we could fill in the *T link once and reuse it instead of using the map. So it seems like in whatever important case we have, we could make PtrTo faster instead of adding new API here. But would that address the case where you are seeing this? |
Change https://golang.org/cl/264237 mentions this issue: |
I was curious how hard this would be, so I wrote https://golang.org/cl/264237 to record pointer types in the ptrToThis field of newly created dynamic types. It would be interesting to know what effect this has on the benchmarks here. |
Actually that code is slower. Never mind. |
Maybe we should re-expand ptrToThis to be a full pointer. I don't know how much this specific field would expand the reflect data. But the more important question is again, what is the context in which this slow execution occurs? /cc @yazver |
Does anyone want to describe a time when the speed of NewAt was too slow? |
We think we know how to optimize NewAt if it is too slow. But we need to see a use case to understand the tradeoff there. On the new API topic (this issue), based on the discussion above this seems like a likely decline. |
No change in consensus, so declined. |
The function
reflect.NewAt
works very slowly. Because it callsptrTo
to convert type to a pointer type,ptrTo
usessync.Map
internally to find a pointer type data. There is no way to pre-create a pointer type and reuse it. Also some cases needs to create a value directly at pointer. The following functions solve this problem.Code: https://github.com/yazver/go/blob/reflect-newatptr/src/reflect/value.go#L2406-L2433
Tests: https://github.com/yazver/go/blob/reflect-newatptr/src/reflect/all_test.go#L7358-L7393.
Benchmark tests for two cases:
Value
fromunsafe.Pointer
(ValueAt
10 times faster thenNewAt
);Value
of a pointer toValue
fromunsafe.Pointer
(NewAtPtr
10 times faster thenNewAt
);Sources of tests:
The text was updated successfully, but these errors were encountered: