Skip to content

Optimizations of Domain build (#83) #277

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

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

Conversation

SergeiPavlov
Copy link
Contributor

  • Optimize TypeInfo.Validators

  • Optimization: create FieldInfo.Assocations on demand

  • Avoid unnecessary .ToList() in TopologicalSorter.Sort()

  • DO_SAFE_COLLECTION_WRAPPER constant

  • readonly struct ColumnIndexMap

  • Optimize IReadOnlyList in SelectProvider

* Optimize TypeInfo.Validators

* Optimization: create FieldInfo.Assocations on demand

* Avoid unnecessary .ToList() in TopologicalSorter.Sort()

* DO_SAFE_COLLECTION_WRAPPER constant

* readonly struct ColumnIndexMap

* Optimize IReadOnlyList in SelectProvider
Comment on lines 562 to 581
public IReadOnlyList<AssociationInfo> Associations => (IReadOnlyList<AssociationInfo>)associations ?? Array.Empty<AssociationInfo>();

public bool ContainsAssociation(string associationName) => associations?.Contains(associationName) == true;

public void AddAssociation(AssociationInfo association) =>
EnsureAssociations().Add(association);

public void AddAssociations(IReadOnlyList<AssociationInfo> range)
{
get { return associations; }
if (range.Count > 0) {
EnsureAssociations().AddRange(range);
}
}

public void RemoveAssociation(AssociationInfo association) =>
_ = EnsureAssociations().Remove(association);

private NodeCollection<AssociationInfo> EnsureAssociations() =>
associations ??= new NodeCollection<AssociationInfo>(this, "Associations");

Copy link
Contributor

@alex-kulakov alex-kulakov Jul 25, 2023

Choose a reason for hiding this comment

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

I suggest removing all these new members and do following in FieldInfo ctor:

private FieldInfo(TypeInfo declaringType, TypeInfo reflectedType, FieldAttributes attributes)
    {
      Attributes = attributes;
      this.declaringType = declaringType;
      this.reflectedType = reflectedType;
      Fields = IsEntity || IsStructure 
        ? new FieldInfoCollection(this, "Fields") 
        : FieldInfoCollection.Empty;

      associations = (IsEntity || IsEntitySet) ? new NodeCollection<AssociationInfo>(this, "Associations") : NodeCollection<AssociationInfo>.Empty;
    }

This makes only the fields which suppose to have association have them, for the rest of the fields (of primitive types or persistent structure types) it will be reference to single object which is locked and can't be changes.

Copy link
Contributor

Choose a reason for hiding this comment

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

I did tests on Xtensive.Orm.Tests.Upgrade.HugeModelUpgrade.RegularModel types set
The change in previous comment helped to reduce number of NodeCollection<AssociationInfo> instances from 8613 to 4269.

Next change helped to squeeze some more water out of stone and reach 2135 instances. This will vary because of model but still will reduce count of instances.

So, the change...
I suggest to improve AssociationInfo.Ancestors collection by doing following...
First, declare the property like so

    public NodeCollection<AssociationInfo> Ancestors
    {
      get { return ancestors ??= new NodeCollection<AssociationInfo>(this, "Ancestors"); }
    }

and remove property initialization from AssociationInfo ctor.

Second, override Lock within the class like so

    public override void Lock(bool recursive)
    {
      base.Lock(recursive);
      if (!recursive)
        return;
      if (ancestors is null || ancestors.Count == 0)
        ancestors = NodeCollection<AssociationInfo>.Empty;
      else
        ancestors.Lock(false);
    }

which shrinks final model. Both cases in IF are possible.

Going further, we can improve all mentions like association.Ancestors.AddRange(...) with check for count before accessing Ancestors property:

    if (field.Associations.Count > 0)
      association.Ancestors.AddRange(field.Associations);

which also can affect some models. The model which I used have no interfaces so it is not affected by this but among all tests there are scenarios when association.Ancestors.AddRange(...) is called for empty item source collection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done all these changes

Copy link
Contributor

Choose a reason for hiding this comment

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

No, not all these changes applied otherwise there wouldn't be
public IReadOnlyList<AssociationInfo> Associations => associations; is still exists. I suggested to revert the region I mentioned above completely and so API would stay untouched.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it

Reverted these methods

Copy link
Contributor

@alex-kulakov alex-kulakov Jul 26, 2023

Choose a reason for hiding this comment

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

Maybe it was my fault and I was unclear. Let me explain what I suggested clearer. FieldInfo.Associations should be

public NodeCollection<AssociationInfo> Associations  => associations;

Methods

  • FieldInfo.ContainsAssociation(string),
  • FieldInfo.AddAssociation(AssociationInfo),
  • FieldInfo.AddAssociations(IReadOnlyList<AssociationInfo>),
  • FieldInfo.RemoveAssociation(AssociationInfo)

should be removed and usages of the methods should be reverted to usage of NodeCollection<AssociationInfo> API.

I see advantage of having IReadOnlyList<AssociationInfo> - it makes it seem read-only. But there are some disadvantages - it is in use during Domain life quite a lot, and every time the collection should be casted to interface before return which would hurt performance and I see you prefer not to allow such casts; also interface doesn't make it read-only and anyone can see what type is underneath and nothing prevents from casting it to NodeCollection<AssociationInfo> and call methods, so only locking makes it read-only and it happens anyway.

So, we don't need to return NodeCollection<AssociationInfo> as interface. It makes working with this collection awkward and we would be forced to bloat API with useless proxy methods or properties to perform operations on it. Also, we would probably break some user code.

Copy link
Contributor Author

@SergeiPavlov SergeiPavlov Jul 26, 2023

Choose a reason for hiding this comment

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

I agree and as I said the suggested changes were made.
And I simplified the property to:
public NodeCollection<AssociationInfo> Associations { get; private set; }

No need for separate field associations

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