-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
7x/field resolver performance (#3668)
* Use custom tostring visitor to create _comparisonValue on Field * Combine variable visitor with new tostring visitor (cherry picked from commit 0fb1149)
- Loading branch information
Showing
10 changed files
with
176 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
src/Nest/CommonAbstractions/Infer/Field/ToStringExpressionVisitor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Text; | ||
|
||
namespace Nest | ||
{ | ||
internal class ToStringExpressionVisitor : ExpressionVisitor | ||
{ | ||
private readonly Stack<string> _stack = new Stack<string>(); | ||
|
||
public bool Cachable { get; private set; } = true; | ||
|
||
public string Resolve(Expression expression, bool toLastToken = false) | ||
{ | ||
Visit(expression); | ||
if (toLastToken) return _stack.Last(); | ||
|
||
return _stack | ||
.Aggregate( | ||
new StringBuilder(), | ||
(sb, name) => | ||
(sb.Length > 0 ? sb.Append(".") : sb).Append(name)) | ||
.ToString(); | ||
} | ||
|
||
public string Resolve(MemberInfo info) => info == null ? null : info.Name; | ||
|
||
protected override Expression VisitMember(MemberExpression expression) | ||
{ | ||
if (_stack == null) return base.VisitMember(expression); | ||
|
||
var name = Resolve(expression.Member); | ||
_stack.Push(name); | ||
return base.VisitMember(expression); | ||
} | ||
|
||
protected override Expression VisitMethodCall(MethodCallExpression methodCall) | ||
{ | ||
if (methodCall.Method.Name == nameof(SuffixExtensions.Suffix) && methodCall.Arguments.Any()) | ||
{ | ||
VisitConstantOrVariable(methodCall, _stack); | ||
var callingMember = new ReadOnlyCollection<Expression>( | ||
new List<Expression> { { methodCall.Arguments.First() } } | ||
); | ||
Visit(callingMember); | ||
return methodCall; | ||
} | ||
else if (methodCall.Method.Name == "get_Item" && methodCall.Arguments.Any()) | ||
{ | ||
var t = methodCall.Object.Type; | ||
var isDict = | ||
typeof(IDictionary).IsAssignableFrom(t) | ||
|| typeof(IDictionary<,>).IsAssignableFrom(t) | ||
|| t.IsGeneric() && t.GetGenericTypeDefinition() == typeof(IDictionary<,>); | ||
|
||
if (!isDict) return base.VisitMethodCall(methodCall); | ||
|
||
VisitConstantOrVariable(methodCall, _stack); | ||
Visit(methodCall.Object); | ||
return methodCall; | ||
} | ||
else if (IsLinqOperator(methodCall.Method)) | ||
{ | ||
for (var i = 1; i < methodCall.Arguments.Count; i++) Visit(methodCall.Arguments[i]); | ||
Visit(methodCall.Arguments[0]); | ||
return methodCall; | ||
} | ||
return base.VisitMethodCall(methodCall); | ||
} | ||
|
||
private void VisitConstantOrVariable(MethodCallExpression methodCall, Stack<string> stack) | ||
{ | ||
var lastArg = methodCall.Arguments.Last(); | ||
if (lastArg is ConstantExpression constantExpression) | ||
{ | ||
stack.Push(constantExpression.Value.ToString()); | ||
return; | ||
} | ||
if (lastArg is MemberExpression memberExpression) | ||
{ | ||
Cachable = false; | ||
stack.Push(memberExpression.Member.Name); | ||
return; | ||
} | ||
Cachable = false; | ||
stack.Push(lastArg.ToString()); | ||
} | ||
|
||
private static bool IsLinqOperator(MethodInfo methodInfo) | ||
{ | ||
if (methodInfo.DeclaringType != typeof(Queryable) && methodInfo.DeclaringType != typeof(Enumerable)) | ||
return false; | ||
|
||
return methodInfo.GetCustomAttribute<ExtensionAttribute>() != null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/Tests/Tests.Benchmarking/ExpressionResolverBenchmarkTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using BenchmarkDotNet.Attributes; | ||
using Elasticsearch.Net; | ||
using Nest; | ||
using Tests.Benchmarking.Framework; | ||
using Tests.Core.Client; | ||
using Tests.Domain; | ||
|
||
namespace Tests.Benchmarking | ||
{ | ||
[BenchmarkConfig] | ||
public class ExpressionResolverBenchmarkTests | ||
{ | ||
|
||
[GlobalSetup] | ||
public void Setup() => Client = TestClient.DefaultInMemoryClient; | ||
|
||
public IElasticClient Client { get; set; } | ||
|
||
[Benchmark(Description = "Boxed Expression", OperationsPerInvoke = 1000)] | ||
public void ResolveBoxedExpressionToString() => ResolveBoxedExpressionToStringImp<Project>(p => p.Suggest.Weight); | ||
|
||
[Benchmark(Description = "Unboxed Expression", OperationsPerInvoke = 1000)] | ||
public void ResolvedUnboxedExpressionToString() => ResolvedUnboxedExpressionToStringImp<Project, int?>(p => p.Suggest.Weight); | ||
|
||
[Benchmark(Description = "Field Resolver", OperationsPerInvoke = 1000)] | ||
public void FieldResolver() => FieldResolverImp<Project>(p => p.Suggest.Weight); | ||
|
||
[Benchmark(Description = "Field Resolver Unboxed", OperationsPerInvoke = 1000)] | ||
public void FieldResolverUnboxed() => FieldResolverUnboxedImp<Project, int?>(p => p.Suggest.Weight); | ||
|
||
private string ResolveBoxedExpressionToStringImp<T>(Expression<Func<T, object>> expression) => expression.ToString(); | ||
|
||
private string ResolvedUnboxedExpressionToStringImp<T, TValue>(Expression<Func<T, TValue>> expression) => expression.ToString(); | ||
|
||
private string FieldResolverImp<T>(Expression<Func<T, object>> expression) => Client.Infer.Field(expression); | ||
|
||
private string FieldResolverUnboxedImp<T, TValue>(Expression<Func<T, TValue>> expression) => Client.Infer.Field(expression); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters