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

Remove redundant abstract/virtual properties, only one is needed. #27

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

deipax
Copy link
Contributor

@deipax deipax commented Oct 25, 2017

No description provided.

When cloning Primitives all that needs to be done is copy the source
array to the target array.  Instead of calling the
PrimitiveTypeExpressionFactory call for every element you can use the
static Array.Copy method to assist.

Depending upon the primitive type, the performance gain is different.
In the tests I have, which are coded against 4.6.1, will have a
performance gain of any where from about 98% to almost 225%.  Byte
Array, for whatever reason, has a performance gain of about 6800% ... I
have no idea why byte arrays are so much different.

Int Arrays go from about 2,386,634 ops per sec to 4,727,659 (98% increase)
String arrays go from about 1,926,782 ops per sec to 5,730,086 (197% increase)
TimeSpan arrays go from about 1,988,071 ops per sec to 4,650,697 (133% increase)
DateTime arrays go from about 1,930,501 ops per sec to 4,210,105 (118% increase)
Delegate arrays go from about 1,659,751 ops per sec to 5,404,864 (225% increase)
Byte arrays go from about 58,083 ops per sec to 4,053,648 (6879% increase)

I would like supply a unit test to easily display the difference in
performance in the master project, but given the interface, it is not
exactly straight forward.  I generated the numbers above from running
the changes suggested from a local release build in my current test
setup.
In order to support polymorphism, we have to find the runtime type and run the appropriate CloneDelegate against the source object.  I added the CloneIdDelegateCache to assist with this process.  I needed to make a couple changes to the ComplexTypeExpression factory to add polymorphism support.  In order to maintain backwards compatibility, if an initializer was supplied by the user, that should be used before the call to the new code.  I added a new test class that asserts several scenarios, including the scenario outlined in issue MarcinJuraszek#7.  Also in the new unit test, I added some SpeedComparions tests  (which I commented out for integration purposes) to help give an idea of what type of performance hit this change will cause.

Let me know what you think.
Similar to the Array of primitives, there is no need to call the clone method for primitivies.  Since List is so common, providing a custom Expression factory should be quite useful.  According to my testing, here are the performance differences:

List of Strings:   22,815 ops per second ->1,111,000 (about 4700% increase)
List of Bytes:  46,153 ops per second ->1,199,880 (about 2500% increase)
List of Ints: 44,863 ops per second -> 990,000 (about 2100% increase)
List of Delegates: 1,119,194 ops per second ->5,221,409 (about 365% increase)
List of DateTime: 1,274,697 ops per second -> 4,629,166 (about 263% increase)
List of TimeSpan: 1,251,564 ops per second -> 5,221,409 (about 317% increase)

One thing I had to do in order to address an assertion made by the unit tests was to not clone the element is the Flag.CollectionItems was missing.  Which I found surpising, should there be such an assertion of Arrays of items as well?  Currently the Array expression factorys do not check flags at all.
…tly. This change set removes all cloning calls for primitives. Here is a incomplete summary of the performance changes (all figures are in operations per second).

Clone an Array of classes: 18,761 ->26,702 (about 42% increase)
Clone an Array of Structs: 27,461 ->40,526 (about 47% increase)
Clone a simple class (all primitive properties): 1,973,684 -> 2,918,287 (about 47% increase)
Clone a complex class: 1,776,198 -> 2,056,202 (about 15% increase)
Clone a Class:List<Class>: 0.999 -> 1.176 (about 17% increase)
Clone a Class:List<int>: 1.608 -> 3.115 (about 93% increase)
Clone a KeyValue<int,int>: 9,569,377 -> 14,903,129 (about 55% increase)
Clone a Tuple<int,int>: 3,992,015 -> 10,033,444 (about 151% increase)
Clone a Nullable<int>: 19,607,843 -> 33,025,099 (about a 68% increase)
…ses. I added Check for nulls. Add them to the cloned object dictionary and reuse them if found. Added unit tests to verify this functionality.
@MarcinJuraszek
Copy link
Owner

.NET allows you to mark a member which hides another member down the inheritance chain and mark that same member virtual to start another chain. I think your current code would not work properly for that scenario.

    public class A
    {
        public virtual string Foo { get; set; }
    }

    public class B : A
    {
        public override string Foo { get; set; }
    }

    public class C : B
    {
        public virtual new string Foo { get; set; }
    }

    public class D : C
    {
        public override string Foo { get; set; }
    }

@deipax
Copy link
Contributor Author

deipax commented Nov 1, 2017

Marcin, you are correct. I had never thought of doing what you showed here. In order to fix that, the depth of each property needs to be known. I have a fix, though it may seem large it uses extension functions to find the necessary information.

The code is from my personal branch and considering the size of the change, I certainly understand if you would just rather deal with the redundant properties and forego it. I thought, however, that I should at least put forth the code and let you decide. It did not take that much effort to adapt my work.

Do you think, perhaps, we could work on polymorphic support next?

Thank you,
-Jeff

@deipax
Copy link
Contributor Author

deipax commented Nov 17, 2017

Have you had a chance to review this Marcin?

@MarcinJuraszek
Copy link
Owner

I'm sorry, @deipax, I've been out of the country for the most of November. I hope to get to this one over the weekend.

About polymorphic support - can you start a new issue on that?

@deipax
Copy link
Contributor Author

deipax commented Dec 3, 2017

Welcome back Marcin! I believe Issue #7 is about support for polymorphism.

I will begin work on it soon, I will make sure to add benchmarks so we have an idea of what type of performance hit there will be. I tend to believe for cases where polymorphism is not used, fetching the runtime type will have little to no overhead ... for the cases where polymorphism is being use, I believe the overhead will be about a 10% hit. In either case, I will make sure to continue to support user supplied value factory ... if one is supplied, that it will be used first.

-Jeff

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants