Skip to content
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

Cache boxed value type values in ModelBase #1438

Merged
merged 2 commits into from
Oct 13, 2019

Conversation

GeertvanHorrik
Copy link
Member

@GeertvanHorrik GeertvanHorrik commented Oct 12, 2019

Description of Change

Cache boxed values.

Issues Resolved

API Changes

  • ModelBase::SetValue
  • ModelBase::SetValueFastButInsecure
  • ModelBase::GetValueFastButInsecure

Platforms Affected

  • All
  • WPF
  • UWP
  • iOS
  • Android

Behavioral Changes

None

Testing Procedure

PR Checklist

  • I have included examples or tests
  • I have updated the change log
  • I am listed in the CONTRIBUTORS file
  • Rebased on top of the target branch at time of PR
  • Changes adhere to coding standard


/// <summary>
/// Caches boxed objects to minimize the memory footprint for boxed value types.
/// </summary>
public class BoxingCache<T>
where T : struct
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just more of a curiosity. I see you removed the type constraint but would have it made more sense to constrain it against IConvertible? It's supported on all the platforms Catel is targeting and makes more sense than struct for what this is being used for.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, was not aware of this interface. Will investigate, I want to enforce value types.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's perfect for when you want to enforce only primitive types (or anything that implements IConvertible) in an generic since struct can introduce a whole can of worms (also avoids having to do ValueType or IsPrimitive checks elsewhere).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does support reference values as well (e.g. a string), and that's something we definitely don't want to cache). But we can enforce that simply by checking for value type first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now I remember why I didn't enforce it to anything. We use this in this method:

        protected static object GetObjectValue<TValue>(TValue value)
        {
            object objectValue = null;

            if (typeof(TValue).IsValueTypeEx())
            {
                objectValue = BoxingCache<TValue>.Default.GetBoxedValue(value);
            }
            else
            {
                objectValue = value;
            }

            return objectValue;
        }

Then we would have to add the constraint there as well, but we want to support reference types there as well (and at runtime check which path to take).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, maybe I can implement it here:

image

Use typeof(IConvertible).IsAssignableFrom(type) for UAP. Will need to profile performance first though.

Copy link

@ghost ghost Oct 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this may be better then?

public static bool IsValueTypeEx(this Type type)
{
    return (type is IConvertible);
}

Since all primitive types derive from this interface that should be all you'd need. There is the obvious side effect that if someone actually goes through the trouble of implementing IConvertible it ends up returning true but I've never seen anyone actually do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, IsAssignableFrom is an extension method, I wonder whether it does this under the hood anyway:

        public static bool IsAssignableFromEx(this Type type, Type typeToCheck)
        {
            Argument.IsNotNull("type", type);
            Argument.IsNotNull("typeToCheck", typeToCheck);

#if UAP_DEFAULT
            return type.IsAssignableFrom(typeToCheck);
            //return type.GetTypeInfo().IsAssignableFrom(typeToCheck.GetTypeInfo());
#else
            return type.IsAssignableFrom(typeToCheck);
#endif
        }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this may be better then?

public static bool IsValueTypeEx(this Type type)
{
    return (type is IConvertible);
}

Since all primitive types derive from this interface that should be all you'd need. There is the obvious side effect that if someone actually goes through the trouble of implementing IConvertible it ends up returning true but I've never seen anyone actually do that.

This definitely makes sense for UAP, but I think it's still faster to read a property value than to check for type (but not sure, and we are talking nano-optimizations here).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, if there’s serious concerns about performance here (doubtful) dotTrace can be used later.

}

if (!set)
{
var memberMetadata = modelInfo.PropertiesByName[member.Name];
if (memberMetadata != null)
{
((PropertyInfo)memberMetadata.Tag).SetValue(model, member.Value, null);
((PropertyInfo)memberMetadata.Tag).SetValue(model, finalValue, null);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good example of what would benefit from the expression tree setter and would speed up serialization.

@GeertvanHorrik GeertvanHorrik changed the title #1437 Cache boxed value type values in ModelBase Cache boxed value type values in ModelBase Oct 13, 2019
@GeertvanHorrik GeertvanHorrik merged commit 0036c5b into develop Oct 13, 2019
@delete-merged-branch delete-merged-branch bot deleted the feature/1437-Cached-boxing branch October 13, 2019 12:39
@lock
Copy link

lock bot commented Oct 18, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 18, 2019
@GeertvanHorrik GeertvanHorrik removed this from the 5.12.0 milestone Oct 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement caching of boxed values in ModelBase to decrease memory footprint
1 participant