mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Create a new method UnsafeConvertToVersion for faster convert
Only encode/decode will call this path, to allow us to optimize for unsafe operations.
This commit is contained in:
parent
51b624103f
commit
ea7e7a18cb
@ -50,11 +50,11 @@ func BenchmarkPodConversion(b *testing.B) {
|
||||
var result *api.Pod
|
||||
for i := 0; i < b.N; i++ {
|
||||
pod := &items[i%width]
|
||||
versionedObj, err := scheme.ConvertToVersion(pod, *testapi.Default.GroupVersion())
|
||||
versionedObj, err := scheme.UnsafeConvertToVersion(pod, *testapi.Default.GroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
@ -76,11 +76,11 @@ func BenchmarkNodeConversion(b *testing.B) {
|
||||
scheme := api.Scheme
|
||||
var result *api.Node
|
||||
for i := 0; i < b.N; i++ {
|
||||
versionedObj, err := scheme.ConvertToVersion(&node, *testapi.Default.GroupVersion())
|
||||
versionedObj, err := scheme.UnsafeConvertToVersion(&node, *testapi.Default.GroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
@ -104,11 +104,11 @@ func BenchmarkReplicationControllerConversion(b *testing.B) {
|
||||
scheme := api.Scheme
|
||||
var result *api.ReplicationController
|
||||
for i := 0; i < b.N; i++ {
|
||||
versionedObj, err := scheme.ConvertToVersion(&replicationController, *testapi.Default.GroupVersion())
|
||||
versionedObj, err := scheme.UnsafeConvertToVersion(&replicationController, *testapi.Default.GroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
|
||||
if err != nil {
|
||||
b.Fatalf("Conversion error: %v", err)
|
||||
}
|
||||
|
@ -43,10 +43,31 @@ func (t objectTyperToTyper) ObjectKind(obj Object) (*unversioned.GroupVersionKin
|
||||
return &gvk, unversionedType, nil
|
||||
}
|
||||
|
||||
// ObjectTyperToTyper casts the old typer interface to the new typer interface
|
||||
func ObjectTyperToTyper(typer ObjectTyper) Typer {
|
||||
return objectTyperToTyper{typer: typer}
|
||||
}
|
||||
|
||||
// unsafeObjectConvertor implements ObjectConvertor using the unsafe conversion path.
|
||||
type unsafeObjectConvertor struct {
|
||||
*Scheme
|
||||
}
|
||||
|
||||
var _ ObjectConvertor = unsafeObjectConvertor{}
|
||||
|
||||
// ConvertToVersion converts in to the provided outVersion without copying the input first, which
|
||||
// is only safe if the output object is not mutated or reused.
|
||||
func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
return c.Scheme.UnsafeConvertToVersion(in, outVersion)
|
||||
}
|
||||
|
||||
// UnsafeObjectConvertor performs object conversion without copying the object structure,
|
||||
// for use when the converted object will not be reused or mutated. Primarily for use within
|
||||
// versioned codecs, which use the external object for serialization but do not return it.
|
||||
func UnsafeObjectConvertor(scheme *Scheme) ObjectConvertor {
|
||||
return unsafeObjectConvertor{scheme}
|
||||
}
|
||||
|
||||
// fieldPtr puts the address of fieldName, which must be a member of v,
|
||||
// into dest, which must be an address of a variable to which this field's
|
||||
// address can be assigned.
|
||||
|
@ -523,11 +523,80 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UnsafeConvertToVersion will convert in to the provided outVersion if such a conversion is possible,
|
||||
// but does not guarantee the output object does not share fields with the input object. It attempts to be as
|
||||
// efficient as possible when doing conversion.
|
||||
func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
switch t := in.(type) {
|
||||
case *Unknown:
|
||||
t.APIVersion = outVersion.String()
|
||||
return t, nil
|
||||
case *Unstructured:
|
||||
t.SetAPIVersion(outVersion.String())
|
||||
return t, nil
|
||||
case *UnstructuredList:
|
||||
t.SetAPIVersion(outVersion.String())
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// determine the incoming kinds with as few allocations as possible.
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("only pointer types may be converted: %v", t)
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
|
||||
}
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
||||
}
|
||||
|
||||
// if the Go type is also registered to the destination kind, no conversion is necessary
|
||||
for i := range kinds {
|
||||
if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group {
|
||||
setTargetKind(in, kinds[i])
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
|
||||
// type is unversioned, no conversion necessary
|
||||
// it should be possible to avoid this allocation
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind := unversionedKind
|
||||
outKind := outVersion.WithKind(kind.Kind)
|
||||
setTargetKind(in, outKind)
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// allocate a new object as the target using the target kind
|
||||
// TODO: this should look in the target group version and find the first kind that matches, rather than the
|
||||
// first kind registered in typeToGVK
|
||||
kind := kinds[0]
|
||||
kind.Version = outVersion.Version
|
||||
kind.Group = outVersion.Group
|
||||
out, err := s.New(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta
|
||||
flags, meta := s.converter.DefaultMeta(t)
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetKind(out, kind)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
}
|
||||
|
||||
// setTargetVersion is deprecated and should be replaced by use of setTargetKind
|
||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
if gv.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
@ -537,3 +606,14 @@ func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
gvk, _ := raw.ObjectKind(obj)
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind})
|
||||
}
|
||||
|
||||
// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
|
||||
func setTargetKind(obj Object, kind unversioned.GroupVersionKind) {
|
||||
if kind.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
// TODO: look at removing the need to special case this
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
|
||||
return
|
||||
}
|
||||
obj.GetObjectKind().SetGroupVersionKind(kind)
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func NewCodecForScheme(
|
||||
encodeVersion []unversioned.GroupVersion,
|
||||
decodeVersion []unversioned.GroupVersion,
|
||||
) runtime.Codec {
|
||||
return NewCodec(encoder, decoder, scheme, scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion)
|
||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion)
|
||||
}
|
||||
|
||||
// NewCodec takes objects in their internal versions and converts them to external versions before
|
||||
@ -263,21 +263,19 @@ func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unv
|
||||
}
|
||||
|
||||
// Perform a conversion if necessary
|
||||
if gvk.GroupVersion() != targetGV {
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
||||
if err != nil {
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
obj = out
|
||||
}
|
||||
}
|
||||
|
||||
objectKind := obj.GetObjectKind()
|
||||
old := objectKind.GroupVersionKind()
|
||||
objectKind.SetGroupVersionKind(unversioned.GroupVersionKind{Group: targetGV.Group, Version: targetGV.Version, Kind: gvk.Kind})
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
||||
if err != nil {
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
obj = out
|
||||
}
|
||||
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
|
||||
err = c.encoder.EncodeToStream(obj, w, overrides...)
|
||||
// restore the old GVK, in case conversion returned the same object
|
||||
objectKind.SetGroupVersionKind(old)
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user