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

Merge pull request #2826 from pharring/TokenStream

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