Skip to content

A bunch of small improvements related to query translation #392

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

Merged
merged 31 commits into from
May 31, 2024

Conversation

alex-kulakov
Copy link
Contributor

No description provided.

- no hash calculations,
- no overhead of hashed
- if we want to change return type of the method to something
  more specific like IReadOnlyCollection or IReadOnlyList, then
  having arrays will help
- string operation improvements
this allows to create List more efficiently
This allow to have Count of processible requests in CommandFactory.
Current API won't be hurt - new ctors provided and readonly field
extends functionality so it is not a breaking change.
True sequence is List<T> anyway.
- use ToArray(count)/ToList(capacity) if possible
- if used only for foreach replaced with regular for with index
- replaced some of usages with CollectionUtils.RangeToXxx()
more efficient collections operations
+ small QueryHelper improvements
For every Query.All<T> one .MakeGenericMethod required and the result is only visited.
The same root expressions are re-used many times during domain lifetime
The number of items in the cache is finite and limit is the number of entities and interfaces
in domain model (auxilary types included). In future we may change type of cache from concurrent
dictionary to LRU cache and limit it to have only hot query roots represented
- ExpressionComparer uses zipping without Funcs, ValueTuple used instead
- SubqueryDefalutResultRewriter uses type cast only when needed
- Translator.ConstructQueryable is optimized to not have making generic type,
  it uses already given type by methodcallexpression. Additionally, by having
  overload that gets element type, we skip MethodCallExpression construction.
- Small improvements in Translator.VisitSequence. Less operations for checking
  for entity set and no double search for type
- typeof (Func<>) is cached within Translator
- Small improvement for Union/Concat/Intersect/etc. operations by getting
  rid of extra enumerations
- RecordSetHeader.Add(IReadOnlyList<Column>) substitutes counterpart with
  IEnumerable<Column> which produce less memory fluctuations.
- RecordSetHeader.Add(Column) also doesn't use IEnumerable
Some types are more frequent to get accessor than others.
For example
- long, int and guid are frequent key values, entity version values so
  one of them will be in every query.
- boolean fields are probably also frequent type amont model fields
- DateTime/DateTimeOffset can be a time stamp for each row.
- Double and decimal values are about money/amout of something/etc, they
  are more important than some other types.

At the same time unsigned integers and Byte/SByte are, to me, used
less frequently.

It's a small optimization but difference between finding accessor on
first and last check is ~2.5x and we always had to make 5-6 checks before
reaching long which is, i believe, the most frequent key value type.
Avoid copying descriptors when it is possible by using
IReadOnlyList<T> constructor parameter instead of params T[]. Constructors
enumerate collections of descriptors, having option to pass List<T> and
array of T is benifitial.
ReverseList uses backwards for, instead of true reverse
Even in case when we need to copy refs to some collection we don't need it,
Reverse() copies refs to a buffer collection anyway.
@SergeiPavlov
Copy link
Contributor

SergeiPavlov commented May 28, 2024

Please consider following Clone() optimization:
https://github.com/servicetitan/dataobjects-net/pull/237/files

Return type covariance allows to avoid type casting in many cases.

@alex-kulakov
Copy link
Contributor Author

This PR is for 7.0. Do you want me to copy these changes to 7.0 or just merge it to master?

@SergeiPavlov
Copy link
Contributor

SergeiPavlov commented May 29, 2024

This PR is for 7.0. Do you want me to copy these changes to 7.0 or just merge it to master?

I thought you are going to apply the PR's changes to master branch too.

While I see now the 7.0 & master branches diverged and there will be many conflicts.

@alex-kulakov
Copy link
Contributor Author

Well, everything which is done in 6.0/7.0/7.1 happens to appear in master some time because of merges. We need this to propagate at least bug fixes from where they were originated (if version is still supported) to the newest versions.

We have some enterprise customers (or customers of our customers) that still use 6.0 and if there is some issue which is very important for them, I'm expected to address it in 6.0.

This PR contains no breaking changes of publicly available APIs, I didn't make drastic changes deliberately, even if they are performance beneficial. If your PR has some of such changes then I'd prefer to merge it to master.

@SergeiPavlov
Copy link
Contributor

SergeiPavlov commented May 30, 2024

If your PR has some of such changes then I'd prefer to merge it to master.

Changes of .Clone() in my PR are not 100% compatible
For example
var s = (SqlSelect) select.Clone(); in Extensions' code should be converted into
(SqlSelect)((ICloneable)select).Clone();

On the other hand, that usage is rare and trivial to convert

@alex-kulakov
Copy link
Contributor Author

It doesn't matter. Target runtime has no support for covariant returns. I will experiment with your suggestion in the version where it is supported by target runtime.

@alex-kulakov alex-kulakov merged commit ff12ca0 into 7.0 May 31, 2024
@alex-kulakov alex-kulakov deleted the 7.0-perf-imps branch May 31, 2024 13:01
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