diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index b49d1e78f87..ea2fbf99415 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -1280,18 +1280,16 @@ private void EnsureParameterInitialized(TaskPropertyInfo parameter, Lookup looku parameter.Initialized = true; - // PERF: Be careful to avoid unnecessary string allocations. Appending '_taskName + "_" + parameter.Name' happens in both paths, - // but we don't want to allocate the string if we don't need to. - string key = "DisableLogTaskParameter_" + _taskName + "_" + parameter.Name; - - if (string.Equals(lookup.GetProperty(key)?.EvaluatedValue, "true", StringComparison.OrdinalIgnoreCase)) + // Use the precomputed lookup keys from the TaskFactoryWrapper to avoid allocations on the hot path + string key = _taskFactoryWrapper.GetDisableLogTaskParameterKey(parameter.Name); + if (key != null && string.Equals(lookup.GetProperty(key)?.EvaluatedValue, "true", StringComparison.OrdinalIgnoreCase)) { parameter.Log = false; } else { - string metadataKey = "DisableLogTaskParameterItemMetadata_" + _taskName + "_" + parameter.Name; - if (string.Equals(lookup.GetProperty(metadataKey)?.EvaluatedValue, "true", StringComparison.OrdinalIgnoreCase)) + string metadataKey = _taskFactoryWrapper.GetDisableLogTaskParameterItemMetadataKey(parameter.Name); + if (metadataKey != null && string.Equals(lookup.GetProperty(metadataKey)?.EvaluatedValue, "true", StringComparison.OrdinalIgnoreCase)) { parameter.LogItemMetadata = false; } diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index f2d24afd836..7c680004738 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -39,16 +39,32 @@ private struct PropertyData /// public readonly IReadOnlyDictionary PropertyInfoCache; + /// + /// Cache of precomputed DisableLogTaskParameter property keys for each parameter. + /// Maps parameter name to "DisableLogTaskParameter_{taskName}_{parameterName}" + /// + public readonly IReadOnlyDictionary DisableLogTaskParameterKeys; + + /// + /// Cache of precomputed DisableLogTaskParameterItemMetadata property keys for each parameter. + /// Maps parameter name to "DisableLogTaskParameterItemMetadata_{taskName}_{parameterName}" + /// + public readonly IReadOnlyDictionary DisableLogTaskParameterItemMetadataKeys; + public PropertyData( IReadOnlyDictionary namesOfPropertiesWithRequiredAttribute, IReadOnlyDictionary namesOfPropertiesWithOutputAttribute, IReadOnlyDictionary namesOfPropertiesWithAmbiguousMatches, - IReadOnlyDictionary propertyInfoCache) + IReadOnlyDictionary propertyInfoCache, + IReadOnlyDictionary disableLogTaskParameterKeys, + IReadOnlyDictionary disableLogTaskParameterItemMetadataKeys) { NamesOfPropertiesWithRequiredAttribute = namesOfPropertiesWithRequiredAttribute; NamesOfPropertiesWithOutputAttribute = namesOfPropertiesWithOutputAttribute; NamesOfPropertiesWithAmbiguousMatches = namesOfPropertiesWithAmbiguousMatches; PropertyInfoCache = propertyInfoCache; + DisableLogTaskParameterKeys = disableLogTaskParameterKeys; + DisableLogTaskParameterItemMetadataKeys = disableLogTaskParameterItemMetadataKeys; } } @@ -168,6 +184,26 @@ public string Name /// public TaskHostParameters FactoryIdentityParameters => _factoryIdentityParameters; + /// + /// Gets the precomputed DisableLogTaskParameter property key for a parameter. + /// Returns null if the parameter name is not found. + /// + internal string? GetDisableLogTaskParameterKey(string parameterName) + { + _propertyData.Value.DisableLogTaskParameterKeys.TryGetValue(parameterName, out string? key); + return key; + } + + /// + /// Gets the precomputed DisableLogTaskParameterItemMetadata property key for a parameter. + /// Returns null if the parameter name is not found. + /// + internal string? GetDisableLogTaskParameterItemMetadataKey(string parameterName) + { + _propertyData.Value.DisableLogTaskParameterItemMetadataKeys.TryGetValue(parameterName, out string? key); + return key; + } + #endregion #region Methods. @@ -265,6 +301,8 @@ private PropertyData PopulatePropertyInfo() Dictionary? namesOfPropertiesWithRequiredAttribute = null; Dictionary? namesOfPropertiesWithOutputAttribute = null; Dictionary? namesOfPropertiesWithAmbiguousMatches = null; + Dictionary? disableLogTaskParameterKeys = null; + Dictionary? disableLogTaskParameterItemMetadataKeys = null; bool taskTypeImplementsIGeneratedTask = typeof(IGeneratedTask).IsAssignableFrom(_taskFactory.TaskType); TaskPropertyInfo[] propertyInfos = _taskFactory.GetTaskParameters(); @@ -325,13 +363,28 @@ private PropertyData PopulatePropertyInfo() // we have a output attribute defined, keep a record of that namesOfPropertiesWithOutputAttribute[propertyInfo.Name] = String.Empty; } + + // Precompute the DisableLogTaskParameter lookup keys to avoid allocations in the hot path + if (disableLogTaskParameterKeys == null) + { + disableLogTaskParameterKeys = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + disableLogTaskParameterKeys[propertyInfo.Name] = "DisableLogTaskParameter_" + _taskName + "_" + propertyInfo.Name; + + if (disableLogTaskParameterItemMetadataKeys == null) + { + disableLogTaskParameterItemMetadataKeys = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + disableLogTaskParameterItemMetadataKeys[propertyInfo.Name] = "DisableLogTaskParameterItemMetadata_" + _taskName + "_" + propertyInfo.Name; } return new PropertyData( (IReadOnlyDictionary?)namesOfPropertiesWithRequiredAttribute ?? ReadOnlyEmptyDictionary.Instance, (IReadOnlyDictionary?)namesOfPropertiesWithOutputAttribute ?? ReadOnlyEmptyDictionary.Instance, (IReadOnlyDictionary?)namesOfPropertiesWithAmbiguousMatches ?? ReadOnlyEmptyDictionary.Instance, - (IReadOnlyDictionary?)propertyInfoCache ?? ReadOnlyEmptyDictionary.Instance); + (IReadOnlyDictionary?)propertyInfoCache ?? ReadOnlyEmptyDictionary.Instance, + (IReadOnlyDictionary?)disableLogTaskParameterKeys ?? ReadOnlyEmptyDictionary.Instance, + (IReadOnlyDictionary?)disableLogTaskParameterItemMetadataKeys ?? ReadOnlyEmptyDictionary.Instance); } #endregion }