提交 ed77aaee 编写于 作者: P Paul Harrington

Merge pull request #2826 from pharring/TokenStream

Allocation reductions in formatting.
......@@ -443,18 +443,19 @@ private SyntaxToken FindCorrectBaseTokenOfRelativeIndentBlockOperation(IndentBlo
// always create task 1 more than current processor count
var partitions = partitioner.GetPartitions(this.TaskExecutor == TaskExecutor.Synchronous ? 1 : Environment.ProcessorCount + 1);
var tasks = new List<Task>(
partitions.Select(
partition =>
this.TaskExecutor.StartNew(
() =>
var tasks = new Task[partitions.Count];
for (int i = 0; i < partitions.Count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
partition.Do(operationPair => ApplySpaceAndWrappingOperationsBody(context, tokenStream, operationPair, applier, cancellationToken));
},
cancellationToken)));
var partition = partitions[i];
tasks[i] = this.TaskExecutor.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
partition.Do(operationPair => ApplySpaceAndWrappingOperationsBody(context, tokenStream, operationPair, applier, cancellationToken));
},
cancellationToken);
}
Task.WaitAll(tasks.ToArray(), cancellationToken);
Task.WaitAll(tasks, cancellationToken);
}
}
......
......@@ -62,15 +62,21 @@ private IList<TextChange> CreateTextChanges(CancellationToken cancellationToken)
using (Logger.LogBlock(FunctionId.Formatting_CreateTextChanges, cancellationToken))
{
var data = this.TokenStream.GetTriviaDataWithTokenPair(cancellationToken);
var result = this.TaskExecutor
.Filter(data, d => d.Item2.ContainsChanges, d => d, cancellationToken)
.SelectMany(d => CreateTextChange(d.Item1.Item1, d.Item1.Item2, d.Item2));
return result.ToList();
var filtered = this.TaskExecutor
.Filter(data, d => d.Item2.ContainsChanges, d => d, cancellationToken);
var result = new List<TextChange>();
foreach (var f in filtered)
{
AddTextChanges(result, f.Item1.Item1, f.Item1.Item2, f.Item2);
}
return result;
}
}
private IEnumerable<TextChange> CreateTextChange(SyntaxToken token1, SyntaxToken token2, TriviaData data)
private void AddTextChanges(List<TextChange> list, SyntaxToken token1, SyntaxToken token2, TriviaData data)
{
var span = TextSpan.FromBounds(token1.RawKind == 0 ? this.TreeInfo.StartPosition : token1.Span.End, token2.RawKind == 0 ? this.TreeInfo.EndPosition : token2.SpanStart);
var originalString = this.TreeInfo.GetTextBetween(token1, token2);
......@@ -78,7 +84,7 @@ private IEnumerable<TextChange> CreateTextChange(SyntaxToken token1, SyntaxToken
foreach (var change in data.GetTextChanges(span))
{
var oldText = (change.Span == span) ? originalString : originalString.Substring(change.Span.Start - span.Start, change.Span.Length);
yield return change.SimpleDiff(oldText);
list.Add(change.SimpleDiff(oldText));
}
}
......
......@@ -35,7 +35,7 @@ private string CreateString(string newLine)
builder.Append(newLine);
}
builder.Append(this.Spaces.CreateIndentationString(this.OptionSet.GetOption(FormattingOptions.UseTabs, this.Language), this.OptionSet.GetOption(FormattingOptions.TabSize, this.Language)));
builder.AppendIndentationString(this.Spaces, this.OptionSet.GetOption(FormattingOptions.UseTabs, this.Language), this.OptionSet.GetOption(FormattingOptions.TabSize, this.Language));
return StringBuilderPool.ReturnAndFree(builder);
}
......
......@@ -9,62 +9,35 @@ namespace Microsoft.CodeAnalysis.Formatting
internal partial class TokenStream
{
/// <summary>
/// thread-safe collection that holds onto changes
/// Thread-safe collection that holds onto changes
/// </summary>
private class Changes
private struct Changes
{
public const int BeginningOfTreeKey = -1;
public const int EndOfTreeKey = -2;
private readonly ConcurrentDictionary<int, TriviaData> _map;
// Created lazily
private ConcurrentDictionary<int, TriviaData> _map;
public Changes()
{
_map = new ConcurrentDictionary<int, TriviaData>();
}
public bool Contains(int key)
{
return _map.ContainsKey(key);
}
public TriviaData this[int key]
{
get
{
return _map[key];
}
}
public void Add(int key, TriviaData triviaInfo)
{
Contract.ThrowIfTrue(this.Contains(key));
_map.TryAdd(key, triviaInfo);
}
public void Replace(int key, TriviaData triviaInfo)
{
Contract.ThrowIfFalse(this.Contains(key));
_map[key] = triviaInfo;
}
public void Remove(int pairIndex)
public bool TryRemove(int pairIndex)
{
TriviaData temp;
_map.TryRemove(pairIndex, out temp);
return _map?.TryRemove(pairIndex, out temp) ?? false;
}
public void AddOrReplace(int key, TriviaData triviaInfo)
{
if (this.Contains(key))
{
Replace(key, triviaInfo);
return;
}
// PERF: Set the concurrency level to 1 because, while the dictionary has to be thread-safe,
// there is very little contention in formatting. A lower concurrency level reduces object
// allocations which are used internally by ConcurrentDictionary for locking.
var map = LazyInitialization.EnsureInitialized(ref _map, () => new ConcurrentDictionary<int, TriviaData>(concurrencyLevel: 1, capacity: 8));
map[key] = triviaInfo;
}
Add(key, triviaInfo);
public bool TryGet(int key, out TriviaData triviaInfo)
{
triviaInfo = null;
return _map?.TryGetValue(key, out triviaInfo) ?? false;
}
}
}
......
......@@ -39,7 +39,7 @@ internal partial class TokenStream
private readonly OptionSet _optionSet;
// hold onto information that are made to original trivia info
private readonly Changes _changes;
private Changes _changes;
// factory that will cache trivia info
private readonly AbstractTriviaDataFactory _factory;
......@@ -65,7 +65,6 @@ public TokenStream(TreeData treeData, OptionSet optionSet, TextSpan spanToFormat
Contract.Requires(this.TokenCount > 0);
// initialize trivia related info
_changes = new Changes();
_cachedOriginalTriviaInfo = new TriviaData[this.TokenCount - 1];
_tokenToIndexMap = new Dictionary<SyntaxToken, int>(this.TokenCount);
......@@ -268,28 +267,14 @@ public void ApplyChange(int pairIndex, TriviaData data)
// do reference equality check
var sameAsOriginal = GetOriginalTriviaData(pairIndex) == data;
if (_changes.Contains(pairIndex))
if (sameAsOriginal)
{
if (sameAsOriginal)
{
_changes.Remove(pairIndex);
return;
}
// okay it already exist.
// replace existing one
_changes.Replace(pairIndex, data);
return;
_changes.TryRemove(pairIndex);
}
// triviaInfo is same as original, nothing to do here.
if (sameAsOriginal)
else
{
return;
_changes.AddOrReplace(pairIndex, data);
}
_changes.Add(pairIndex, data);
}
public int GetCurrentColumn(SyntaxToken token)
......@@ -465,9 +450,10 @@ public TriviaData GetTriviaDataAtBeginningOfTree()
{
Contract.ThrowIfFalse(this.FormatBeginningOfTree);
if (_changes.Contains(Changes.BeginningOfTreeKey))
TriviaData data;
if (_changes.TryGet(Changes.BeginningOfTreeKey, out data))
{
return _changes[Changes.BeginningOfTreeKey];
return data;
}
Contract.Requires(_treeData.IsFirstToken(this.FirstTokenInStream.Token));
......@@ -478,9 +464,10 @@ public TriviaData GetTriviaDataAtEndOfTree()
{
Contract.ThrowIfFalse(this.FormatEndOfTree);
if (_changes.Contains(Changes.EndOfTreeKey))
TriviaData data;
if (_changes.TryGet(Changes.EndOfTreeKey, out data))
{
return _changes[Changes.EndOfTreeKey];
return data;
}
Contract.Requires(_treeData.IsLastToken(this.LastTokenInStream.Token));
......@@ -491,9 +478,10 @@ public TriviaData GetTriviaData(int pairIndex)
{
Contract.ThrowIfFalse(0 <= pairIndex && pairIndex < this.TokenCount - 1);
if (_changes.Contains(pairIndex))
TriviaData data;
if (_changes.TryGet(pairIndex, out data))
{
return _changes[pairIndex];
return data;
}
// no change between two tokens, return trivia info from original code
......
......@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Text;
namespace Microsoft.CodeAnalysis.Formatting
{
......@@ -122,6 +123,20 @@ public static string CreateIndentationString(this int desiredIndentation, bool u
return new string('\t', numberOfTabs) + new string(' ', numberOfSpaces);
}
public static StringBuilder AppendIndentationString(this StringBuilder sb, int desiredIndentation, bool useTab, int tabSize)
{
int numberOfTabs = 0;
int numberOfSpaces = Math.Max(0, desiredIndentation);
if (useTab)
{
numberOfTabs = desiredIndentation / tabSize;
numberOfSpaces -= numberOfTabs * tabSize;
}
return sb.Append('\t', repeatCount: numberOfTabs).Append(' ', repeatCount: numberOfSpaces);
}
public static void ProcessTextBetweenTokens(
this string text,
TreeData treeInfo,
......@@ -168,9 +183,8 @@ public static string CreateIndentationString(this int desiredIndentation, bool u
}
var newIndentation = GetNewIndentationForComments(triviaText, nonWhitespaceCharIndex, forceIndentation, indentation, indentationDelta, tabSize);
var newIndentationString = newIndentation.CreateIndentationString(useTab, tabSize);
builder.Append(newIndentationString);
builder.AppendIndentationString(newIndentation, useTab, tabSize);
if (!isEmptyString)
{
builder.Append(triviaText, nonWhitespaceCharIndex, triviaText.Length - nonWhitespaceCharIndex);
......@@ -209,9 +223,7 @@ public static string CreateIndentationString(this int desiredIndentation, bool u
if (nonWhitespaceCharIndex >= 0)
{
var newIndentation = GetNewIndentationForComments(line, nonWhitespaceCharIndex, forceIndentation, indentation, indentationDelta, tabSize);
var newIndentationString = newIndentation.CreateIndentationString(useTab, tabSize);
builder.Append(newIndentationString);
builder.AppendIndentationString(newIndentation, useTab, tabSize);
builder.Append(line, nonWhitespaceCharIndex, line.Length - nonWhitespaceCharIndex);
}
......
......@@ -811,12 +811,12 @@ private string GetWhitespaceString(LineColumn lineColumn, LineColumnDelta delta)
// space indicates indentation
if (delta.Lines > 0 || lineColumn.Column == 0)
{
sb.Append(delta.Spaces.CreateIndentationString(useTabs, tabSize));
sb.AppendIndentationString(delta.Spaces, useTabs, tabSize);
return StringBuilderPool.ReturnAndFree(sb);
}
// space indicates space between two noisy trivia or tokens
sb.Append(GetSpaces(delta.Spaces));
sb.Append(' ', repeatCount: delta.Spaces);
return StringBuilderPool.ReturnAndFree(sb);
}
......
......@@ -35,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
builder.Append(" "c)
builder.Append(SyntaxFacts.GetText(SyntaxKind.LineContinuationTrivia))
builder.Append(Me.Spaces.CreateIndentationString(Me.OptionSet.GetOption(FormattingOptions.UseTabs, LanguageNames.VisualBasic), Me.OptionSet.GetOption(FormattingOptions.TabSize, LanguageNames.VisualBasic)))
builder.AppendIndentationString(Me.Spaces, Me.OptionSet.GetOption(FormattingOptions.UseTabs, LanguageNames.VisualBasic), Me.OptionSet.GetOption(FormattingOptions.TabSize, LanguageNames.VisualBasic))
Return StringBuilderPool.ReturnAndFree(builder)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册