-
Notifications
You must be signed in to change notification settings - Fork 352
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
Perf improvements in FunctionOverloadResolver #1602
Perf improvements in FunctionOverloadResolver #1602
Conversation
src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs
Outdated
Show resolved
Hide resolved
/// <param name="parameters">The list of non-binding parameter names to match.</param> | ||
/// <param name="caseInsensitive">Whether case insensitive.</param> | ||
/// <returns>The best matching operations based on parameters.</returns> | ||
internal static IList<IEdmOperation> FindBestOverloadBasedOnParameters(this IList<IEdmOperation> functions, IList<string> parameters, bool caseInsensitive = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have the Perf telemetry about your change? #Pending
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I will get that once the service adopt the new version. I can try creating new perf tests but that will not be accurate.
In reply to: 352770976 [](ancestors = 352770976)
// The best match out of a list of candidates is the one that has the same number of (non-binding) parameters as specified. | ||
for(int i=0; i < functions.Count; i++) | ||
{ | ||
if (functions[i].Parameters.Count() == parameters.Count + (functions[i].IsBound ? 1 : 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- How about to get rid of "functions[i].Parameters.Count()"
- same "parameters.Count" into an int and use it here maybe better to call the property anytime. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IEnumerable does not have a count property. A cast to List will also be expensive. Am I missing something here?
In reply to: 352772232 [](ancestors = 352772232)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. here i mean the perf about the "[i]" on the IList.
In reply to: 353939228 [](ancestors = 353939228,352772232)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is random access. underlying structure for List is an array. Since this is internally used, we know that IList is implemented by an array based backing field.
In reply to: 363453414 [](ancestors = 363453414,353939228,352772232)
/// <param name="parameters">The list of non-binding parameter names to match.</param> | ||
/// <param name="caseInsensitive">Whether case insensitive.</param> | ||
/// <returns>The best matching operations based on parameters.</returns> | ||
internal static IList<IEdmOperation> FindBestOverloadBasedOnParameters(this IList<IEdmOperation> functions, IList<string> parameters, bool caseInsensitive = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where's usage of "caseInsensitive"? #Resolved
/// <param name="parameters">The list of non-binding parameter names to match.</param> | ||
/// <param name="caseInsensitive">Whether case insensitive.</param> | ||
/// <returns>The best matching operations based on parameters.</returns> | ||
internal static IList<IEdmOperation> FindBestOverloadBasedOnParameters(this IList<IEdmOperation> functions, IList<string> parameters, bool caseInsensitive = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if only compare the count of parameters, why don't you input the number of parameter? #Resolved
|
||
foreach (var operation in operations) | ||
{ | ||
bool suitableCandidate = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this line after if(...) #Resolved
@@ -1829,20 +1926,32 @@ private static bool ParametersSatisfyFunction(IEdmOperation operation, IList<str | |||
List<IEdmOperationParameter> functionParameters = parametersToMatch.ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List functionParameters = parametersToMatch.ToList(); [](start = 12, length = 77)
We can avoid this ToList allocation and use the enumerator in the below loop, since making the List copy is not giving us much in this method and the allocation is the most expensive here.
foreach (var functionParemeter in parametersToMatch)
{
...
} #Resolved
I think we can save these allocations in these method. Something like: var list = operation.Parameters.Where(parameter => string.Equals(identifier, parameter.Name, StringComparison.Ordinal));
Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:412 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
Can't we always do IgnoreCase to avoid two O(n) searches? #Pending Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:416 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
We can also save this allocation. Calling Any(), SingleOrDefault, LastOrDefault will be better than copying an array. #Resolved Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:373 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
If this can be large, it will still be better to save ToList() and instead resort to Count() to compare the length of the arrays. Or do the check as you go through the loop below. #Resolved Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:355 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
Saved this allocation but mind you, to avoid a breaking change, we will have to call Count() on the IEnumerable. However, since the these are keys, the length should be fairly small to iterate. In reply to: 561378338 [](ancestors = 561378338) Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:317 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
Slightly different implementation but I think it might be more performant. In reply to: 561381264 [](ancestors = 561381264) Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:412 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
I suppose there is scope for improvement. However, the logic is that we first try and find the exact case matching parameter and then if nothing is found then find case insensitive parameter. If multiple are found in either of the two searching, we throw an exception. We would need to write additional logic to distinguish between multiple case sensitive and insensitive results; we can probably do that by allocating another list. Since the count of these enumerables will be small, the only cost is for getting the enumerator. The question comes down to 2 gets on enumerators vs 1 list allocation. In reply to: 561381545 [](ancestors = 561381545) Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:416 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
We seem to have multiple places where we check Count() > 1 which for collections that cannot cast to IList<> will result in O(n) algorithm. It would be more effective to create an extension that leverages GetEnumerator to move 2 positions to find whether the array size is >1. It will also save the casts Count() does. #Resolved Refers to: src/Microsoft.OData.Core/UriParser/Binders/SelectPathSegmentTokenBinder.cs:180 in 8b1a788. [](commit_id = 8b1a788, deletion_comment = False) |
@@ -185,7 +185,7 @@ internal static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel mo | |||
// If more than one overload matches, try to select based on optional parameters | |||
if (possibleFunctions.Count() > 1 && parameterNames.Count > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Count [](start = 34, length = 5)
Like this too. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted the CountGreaterThan change. Experimental data showed that .Count() outperforms CountGreaterThan for smaller values.
In reply to: 354091432 [](ancestors = 354091432)
// If any of the things returned are an action, it better be the only thing returned, and there can't be parameters in the URL | ||
bool actionExists = false; | ||
bool functionExists = false; | ||
for (int i = 0; i < candidatesMatchingOperations.Count; i++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for [](start = 12, length = 3)
Should we break the loop as soon as functionExists and/or actionExists ? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Throw an error if key size from url doesn't match that from model. | ||
// Other derived ODataUriResolver intended for alternative key resolution, such as the built in AlternateKeysODataUriResolver, | ||
// should override this ResolveKeys method. | ||
if (keyProperties.Count != positionalValues.Count) | ||
if (type.Key().Count() != positionalValues.Count) | ||
{ | ||
throw ExceptionUtil.CreateBadRequestError(Strings.BadRequest_KeyCountMismatch(type.FullName())); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we optimize for the success scenario I think we could avoid the Count().
- first do the foreach loop while counting how many elements there are in Key().
- after the foreach loop, check if the count of Key() != than positionalValues.Count. Throw exception if so.
Wouldn't this work? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joaocpaiva It might be counted as a breaking change. In that case, we will end up throwing the other exception before we could throw key count mismatch.
I know this sounds like a bad request scenario anyway and people shouldn't care about it but we have had customers complain because their tests started failing after an upgrade.
The other option is to hold off on throwing the second exception before we could loop through completely which might be an overkill and reducing the readability of the code. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that makes sense. It is ok to leave as is then. Alternatively, we could also move the Syntax Error exception after the loop but it does not seem worth the trouble. #Resolved
|
||
// Throw an error if key size from url doesn't match that from model. | ||
// Other derived ODataUriResolver intended for alternative key resolution, such as the built in AlternateKeysODataUriResolver, | ||
// should override this ResolveKeys method. | ||
if (keyProperties.Count != namedValues.Count) | ||
if (type.Key().Count() != namedValues.Count) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Count() [](start = 27, length = 8)
Same comment applies here. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is looking really good! Just left a few more comments that looks like a few more small wins.
.Where(source => string.Equals(identifier, source.Name, StringComparison.OrdinalIgnoreCase)).ToList(); | ||
.Where(source => string.Equals(identifier, source.Name, StringComparison.OrdinalIgnoreCase)); | ||
|
||
IEdmNavigationSource resolvedNavigationSource = default(IEdmNavigationSource); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default(IEdmNavigationSource); [](start = 60, length = 30)
I don't understand what's the performance advantage to use "default(interface)" and "null"? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep the parity. Previously, it was SingleOrDefault, to avoid any breaking changes, I relied on Default here rather than null.
In reply to: 362107858 [](ancestors = 362107858)
Currently, the "resolve" process is two steps:
I think consider to combine 2 steps into one can improve the performance a lot. #Resolved Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:90 in 8b1a788. [](commit_id = 8b1a788, deletion_comment = False) |
{ | ||
string valueText = positionalValues[i]; | ||
IEdmProperty keyProperty = keyProperties[i]; | ||
string valueText = positionalValues[i++]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[i++] [](start = 51, length = 5)
random acess using '[]' in the list maybe a performance problem #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random access is allowed in lists. It does not iterate through the entire list. LinkedList would be the one where random access would be a problem.
In reply to: 362108940 [](ancestors = 362108940)
for (int i = 0; i < keyProperties.Count; i++) | ||
|
||
int i = 0; | ||
foreach (IEdmProperty keyProperty in type.Key()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type.Key() [](start = 49, length = 10)
why call Key() twice in one method. It's also performance #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point @xuzhg - random access is preferable when IList objects boil down to List, since it implements random access as O(1) operation. However, when we are not absolutely sure what's the underlying implementation we should probably do foreach(...) instead of for(...) loop. Moreover, foreach(...) VS for(...) loop is a micro optimization that I don't feel too strong about it either.
To help with this, we should start using the underlying class, instead of the interface (unless when there is a need to send down the interface). #Resolved
{ | ||
// if no case sensitive, try case-insensitive | ||
list = operation.Parameters.Where(parameter => string.Equals(identifier, parameter.Name, StringComparison.OrdinalIgnoreCase)).ToList(); | ||
list = operation.Parameters.Where(parameter => string.Equals(identifier, parameter.Name, StringComparison.OrdinalIgnoreCase)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we combine them as one? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per previous reply, Ordinal string comparison will be cheaper than Ignore case.
In reply to: 362109307 [](ancestors = 362109307)
@@ -3182,19 +3182,12 @@ internal static bool TryGetStaticEntitySet(this IEdmPathExpression pathExpressio | |||
|
|||
internal static bool HasAny<T>(this IEnumerable<T> enumerable) where T : class | |||
{ | |||
IList<T> list = enumerable as IList<T>; | |||
if (list != null) | |||
if (enumerable != null && enumerable.GetEnumerator().MoveNext()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&& enumerable.GetEnumerator().MoveNext()) [](start = 35, length = 41)
can you point me the materials to say to use this to test "HasAny" #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool matched = false; | ||
for (int j = 0; j < parameterNameList.Count; j++) | ||
{ | ||
if (string.Equals(parameterNameList[j], functionParameter.Name, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parameterNameList[j] [](start = 38, length = 20)
same here for the [j] on IList, it's not random access. What's the benefit to access using "index"? Maybe I miss something. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
random access is faster. Lists do support random access.
In reply to: 363456913 [](ancestors = 363456913)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕐
I think we had discussed this offline. Ignore case is twice as expensive check compared to case sensitive. Let's not degrade the performance when someone is specifying the correct case. In reply to: 569810729 [](ancestors = 569810729) Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:90 in 8b1a788. [](commit_id = 8b1a788, deletion_comment = False) |
|
||
private static void EnsureOperationBoundWithBindingParameter(this IEdmOperation operation) | ||
{ | ||
if (!operation.IsBound) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make an assert instead. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converted to assert for functionoverload resolver but did not revert for select segment code path.
In reply to: 370876004 [](ancestors = 370876004)
return true; | ||
} | ||
|
||
if (foundActionsWhenLookingForFunctions.Count > 0) | ||
if (foundActionsWhenLookingForFunctions != null && foundActionsWhenLookingForFunctions.Count > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foundActionsWhenLookingForFunctions [](start = 16, length = 35)
Make this a boolean instead; verify. #Closed
if (actionItems == null) | ||
{ | ||
actionItems = new List<IEdmOperation>(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set a boolean here. #Closed
{ | ||
if (count > 0 && !(param is IEdmOptionalParameter)) | ||
{ | ||
suitableCandidate = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See if edmlib enforces optional parameters at the end then just look for the first optional parameter.
Set suitable to false if we see another required parameter. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EdmLib does not validate the ordering. Just checking for the first optional parameter fails some of our tests.
In reply to: 370881673 [](ancestors = 370881673)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EdmLib does not validate the ordering. Just checking for the first optional parameter fails some of our tests.
In reply to: 370881673 [](ancestors = 370881673)
{ | ||
suitableCandidate = false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine as per Mike's previous comment. #Closed
if (string.Equals(parameterNameList[j], functionParameter.Name, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) | ||
{ | ||
matched = true; | ||
parameterExists[j] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break; #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like you added the break; is there a reason you have to continue looping through the remaining parameter names once you find one that matches?
In reply to: 370884636 [](ancestors = 370884636)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More specifically; this matches the current (flawed) behavior of flagging all case-insensitive matches. Kanish to open a separate issue to track fixing this existing logic.
In reply to: 371616803 [](ancestors = 371616803,371602187,370884636)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
return false; | ||
if (!parameterExists[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parameterExists [](start = 21, length = 15)
Can i do it in O(1)? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
// Filter out functions with any parameters | ||
candidateMatchingOperations = candidateMatchingOperations.Where(o => (o.IsFunction() && !o.Parameters.Any()) || o.IsAction()); | ||
candidatesMatchingOperations = candidatesMatchingOperations.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(bindingType) as IList<IEdmOperation>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null check if list casting fails. #Resolved
if (functionExists && actionExists) | ||
{ | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
split and move to 185; and 190? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't move both the exceptions to above as we need to traverse the entire list before deciding if both action and function exist vs only actions exists.
In reply to: 370888279 [](ancestors = 370888279)
@@ -217,45 +215,58 @@ internal static bool ResolveOperationFromList(string identifier, IEnumerable<str | |||
throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); | |||
} | |||
|
|||
matchingOperation = candidateMatchingOperations.Single(); | |||
matchingOperation = candidatesMatchingOperations.Count > 0 ? candidatesMatchingOperations[0] : null; | |||
return true; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move these up. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't, refactored into a separate function now. Moving both of the exceptions does not improve the code as we need to iterate through all the operations anyway.
In reply to: 370888720 [](ancestors = 370888720)
return true; | ||
} | ||
|
||
if (foundActionsWhenLookingForFunctions.Count > 0) | ||
if (foundActionsWhenLookingForFunctions != null && foundActionsWhenLookingForFunctions.Count > 0) | ||
{ | ||
throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check the validity of this in terms of the position. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of being smart about not matching an action when request uri has parameters, this code throws even if there might be functions matching. Now, we only throw when there are no functions matched as well.
In reply to: 370889470 [](ancestors = 370889470)
} | ||
|
||
return exactMatches == null ? operations : exactMatches; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should return a single item and throw an exception if multiple operations match? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of throwing, I decided to change the name of the function to indicate filtering rather than finding the best overload.
In reply to: 370893088 [](ancestors = 370893088)
@@ -151,12 +169,17 @@ public virtual IEdmTerm ResolveTerm(IEdmModel model, string termName) | |||
|
|||
IList<IEdmTerm> results = FindAcrossModels<IEdmTerm>(model, termName, /*caseInsensitive*/ true); | |||
|
|||
if (results == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
) [](start = 31, length = 1)
Add check for count == 0 and make the return statement cleaner. #Closed
|
||
string caseInsensitiveKey = string.Empty; | ||
int count = 0; | ||
foreach (var key in list) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make it a boolean. #Closed
{ | ||
return model.SchemaElements.OfType<T>() | ||
.Where(e => string.Equals(qualifiedName, e.FullName(), caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); | ||
foreach (var schema in model.SchemaElements) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var [](start = 21, length = 3)
IEDMSchemaElement, look for type in above changes as well. #Resolved
@@ -438,22 +480,36 @@ internal static ODataUriResolver GetUriResolver(IServiceProvider container) | |||
return container.GetRequiredService<ODataUriResolver>(); | |||
} | |||
|
|||
private static List<T> FindAcrossModels<T>(IEdmModel model, String qualifiedName, bool caseInsensitive) where T : IEdmSchemaElement | |||
private static IList<T> FindAcrossModels<T>(IEdmModel model, String qualifiedName, bool caseInsensitive) where T : IEdmSchemaElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private [](start = 8, length = 7)
Is it okay to return null vs an empty list? #Resolved
{ | ||
List<T> results = FindSchemaElements<T>(model, qualifiedName, caseInsensitive).ToList(); | ||
IList<T> results = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove empty list. #Resolved
@@ -3182,19 +3182,12 @@ internal static bool TryGetStaticEntitySet(this IEdmPathExpression pathExpressio | |||
|
|||
internal static bool HasAny<T>(this IEnumerable<T> enumerable) where T : class | |||
{ | |||
IList<T> list = enumerable as IList<T>; | |||
if (list != null) | |||
if (enumerable != null && enumerable.GetEnumerator().MoveNext()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enumerable.GetEnumerator().MoveNext() [](start = 38, length = 37)
return the movenext. #Closed
return false; | ||
} | ||
|
||
internal static bool CountGreaterThan<T>(this IEnumerable<T> enumerable, int count) where T : class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move as private. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed CountGreater than to private but ended up removing that. This is a shared function that gets used in SelectPathSegmentTokenBinder, therefore keeping as internal.
In reply to: 371615196 [](ancestors = 371615196,370898092)
internal static bool CountGreaterThan<T>(this IEnumerable<T> enumerable, int count) where T : class | ||
{ | ||
int currentCount = 0; | ||
foreach (T element in enumerable) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While move next to avoid the null check. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could also do if (enumerable != null) { return enumerable.GetEnumerator().MoveNext();}
In reply to: 370898326 [](ancestors = 370898326)
continue; | ||
} | ||
|
||
currentCount++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make it ++currentCount > count instead. #Closed
results = new List<T>(); | ||
} | ||
|
||
results.Add((T)schema); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(( [](start = 35, length = 2)
Try and get rid of this repeated cast. Removing var might help with that. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does not help. If it is a class, we can but not with an interface.
In reply to: 370899362 [](ancestors = 370899362)
{ | ||
// Binding type requires atleast one parameter for a function. | ||
bool parameterFound = bindingType != null ? false : true; | ||
foreach (var param in operation.Parameters) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var [](start = 33, length = 3)
try to avoid use of var #Closed
Right. The logic for case insensitive is to first match case sensitive. If that fails, attempt to match case insensitive, in which case there can be only one case-insensitive match. This is by protocol (and best practice for handling case-insensitive comparison) In reply to: 561816744 [](ancestors = 561816744,561381545) Refers to: src/Microsoft.OData.Core/UriParser/Resolver/ODataUriResolver.cs:416 in 5adb8e8. [](commit_id = 5adb8e8, deletion_comment = False) |
Reverted the CountGreaterThan change. Experimental data showed that .Count() outperforms CountGreaterThan for smaller values. In reply to: 561946259 [](ancestors = 561946259) Refers to: src/Microsoft.OData.Core/UriParser/Binders/SelectPathSegmentTokenBinder.cs:180 in 8b1a788. [](commit_id = 8b1a788, deletion_comment = False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
9ee197c
to
558d4b1
Compare
Issues
This pull request fixes issue #1392
Description
This PR removes some of the expensive LINQ calls without breaking the public interface.
Checklist (Uncheck if it is not completed)
Results
HasAny previous for enumerables - 0.3372341
HasAny now for enumerables - 0.2167854
Additional work necessary
I would like to add tests for the changes once the initial work is reviewed. More performance improvement related data should be attached with this PR.