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

Avoid allocations from EvaluationTarget.GetId #6872

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using AdaptiveExpressions.Memory;

namespace Microsoft.Bot.Builder.LanguageGeneration
Expand Down Expand Up @@ -46,14 +48,40 @@ public EvaluationTarget(string templateName, IMemory scope)
/// </value>
public IMemory Scope { get; set; }

/// <summary>Throws an exception if any of the specified targets equals the specified id such that a loop is detected.</summary>
/// <param name="targets">The targets to compare.</param>
/// <param name="id">The id against which the targets should be compared.</param>
/// <param name="templateName">The template name to include in any resulting exception.</param>
public static void ThrowIfLoopDetected(
Stack<EvaluationTarget> targets,
(string TemplateName, string ScopeVersion) id,
string templateName)
{
foreach (var target in targets)
{
if (target.TemplateName == id.TemplateName && target.Scope?.Version() == id.ScopeVersion)
{
throw new InvalidOperationException($"{TemplateErrors.LoopDetected} {string.Join(" => ", targets.Reverse().Select(e => e.TemplateName))} => {templateName}");
}
}
}

/// <summary>Combines the components of the specified <paramref name="id"/> to create a string key.</summary>
/// <param name="id">The id retrieved from <see cref="GetId"/>.</param>
/// <returns>The created string key.</returns>
public static string CreateKey((string TemplateName, string ScopeVersion) id)
{
return id.TemplateName + id.ScopeVersion;
}

/// <summary>
/// Get current instance id. If two target has the same Id,
/// we can say they have the same template evaluation.
/// </summary>
/// <returns>Id.</returns>
public string GetId()
public (string TemplateName, string ScopeVersion) GetId()
{
return TemplateName + Scope?.Version();
return (TemplateName, Scope?.Version());
}
}
}
25 changes: 13 additions & 12 deletions libraries/Microsoft.Bot.Builder.LanguageGeneration/Evaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,18 @@ public object EvaluateTemplate(string inputTemplateName, object scope)
var templateTarget = new EvaluationTarget(templateName, memory);
var currentEvaluateId = templateTarget.GetId();

if (_evaluationTargetStack.Any(e => e.GetId() == currentEvaluateId))
{
throw new InvalidOperationException($"{TemplateErrors.LoopDetected} {string.Join(" => ", _evaluationTargetStack.Reverse().Select(e => e.TemplateName))} => {templateName}");
}
EvaluationTarget.ThrowIfLoopDetected(_evaluationTargetStack, currentEvaluateId, templateName);

string key = null;
object result = null;
var hasResult = false;
if (!reExecute)
{
if (_lgOptions.CacheScope == LGCacheScope.Global)
{
if (_cachedResult.ContainsKey(currentEvaluateId))
key ??= EvaluationTarget.CreateKey(currentEvaluateId);
if (_cachedResult.TryGetValue(key, out result))
{
result = _cachedResult[currentEvaluateId];
hasResult = true;
}
}
Expand All @@ -132,9 +130,9 @@ public object EvaluateTemplate(string inputTemplateName, object scope)
{
previousEvaluateTarget = CurrentTarget();

if (previousEvaluateTarget.CachedEvaluatedChildren.ContainsKey(currentEvaluateId))
key ??= EvaluationTarget.CreateKey(currentEvaluateId);
if (previousEvaluateTarget.CachedEvaluatedChildren.TryGetValue(key, out result))
{
result = previousEvaluateTarget.CachedEvaluatedChildren[currentEvaluateId];
hasResult = true;
}
}
Expand Down Expand Up @@ -162,13 +160,15 @@ public object EvaluateTemplate(string inputTemplateName, object scope)
{
if (_lgOptions.CacheScope == LGCacheScope.Global)
{
_cachedResult[currentEvaluateId] = result;
key ??= EvaluationTarget.CreateKey(currentEvaluateId);
_cachedResult[key] = result;
}
else if (_lgOptions.CacheScope == null || _lgOptions.CacheScope == LGCacheScope.Local)
{
if (_evaluationTargetStack.Count > 0)
{
_evaluationTargetStack.Peek().CachedEvaluatedChildren[currentEvaluateId] = result;
key ??= EvaluationTarget.CreateKey(currentEvaluateId);
_evaluationTargetStack.Peek().CachedEvaluatedChildren[key] = result;
}
}
}
Expand Down Expand Up @@ -524,13 +524,14 @@ private EvaluationTarget CurrentTarget() =>
if (currentTemplate != null)
{
var source = currentTemplate.SourceRange.Source;
if (expressionContext != null && _lgOptions.OnEvent != null)
if (expressionContext != null && _lgOptions.OnEvent != null && currentTemplate.Expressions.Count != 0)
{
var lineOffset = currentTemplate.SourceRange.Range.Start.Line;
var sourceRange = new SourceRange(expressionContext, source, lineOffset);
var expressionRef = new ExpressionRef(exp, sourceRange);
var expressionRefId = expressionRef.GetId();

var expression = currentTemplate.Expressions.FirstOrDefault(u => u.GetId() == expressionRef.GetId());
var expression = currentTemplate.Expressions.FirstOrDefault(u => u.Equals(expressionRefId));
if (expression != null)
{
_lgOptions.OnEvent(expression, new BeginExpressionEvaluationArgs { Source = source, Expression = exp });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,7 @@ public List<object> ExpandTemplate(string templateName, object scope)

var templateTarget = new EvaluationTarget(templateName, memory);

var currentEvaluateId = templateTarget.GetId();

if (_evaluationTargetStack.Any(e => e.GetId() == currentEvaluateId))
{
throw new InvalidOperationException($"{TemplateErrors.LoopDetected} {string.Join(" => ", _evaluationTargetStack.Reverse().Select(e => e.TemplateName))} => {templateName}");
}
EvaluationTarget.ThrowIfLoopDetected(_evaluationTargetStack, templateTarget.GetId(), templateName);

// Using a stack to track the evaluation trace
_evaluationTargetStack.Push(templateTarget);
Expand Down
Loading