Created
November 12, 2018 21:05
-
-
Save mrlacey/fd68ddd93446e1c5282afa536fb5780f to your computer and use it in GitHub Desktop.
Code used in benchmark tests for StringResourceVisualizer when reviewing cahnging string manipulation to use Span<T>
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
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Running; | |
namespace StrResVizBenchmarks | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var summary = BenchmarkRunner.Run<StringResourceVisualizerPerf>(); | |
Console.ReadKey(true); | |
} | |
} | |
public class StringResourceVisualizerPerf | |
{ | |
private const string FileText = @" | |
using Microsoft.VisualStudio.Text.Editor; | |
using Microsoft.VisualStudio.Text.Formatting; | |
namespace StringResourceVisualizer | |
{ | |
/// <summary> | |
/// Resizes relevant lines in the editor. | |
/// </summary> | |
internal class MyLineTransformSource : ILineTransformSource | |
{ | |
private readonly ResourceAdornmentManager manager; | |
public MyLineTransformSource(ResourceAdornmentManager manager) | |
{ | |
Debug.WriteLine(Resources1.CreatedMyLineTransformSource); | |
this.manager = manager; | |
} | |
LineTransform ILineTransformSource.GetLineTransform(ITextViewLine line, double yPosition, ViewRelativePosition placement) | |
{ | |
Debug.WriteLine(Resources1.Info_TransformLine); | |
int lineNumber = line.Snapshot.GetLineFromPosition(line.Start.Position).LineNumber; | |
LineTransform lineTransform; | |
// TODO: Don't show if line is collapsed. Issue #3 | |
if (this.manager.DisplayedTextBlocks.ContainsKey(lineNumber)) | |
{ | |
var defaultTopSpace = line.DefaultLineTransform.TopSpace; | |
var defaultBottomSpace = line.DefaultLineTransform.BottomSpace; | |
lineTransform = new LineTransform(defaultTopSpace + ResourceAdornmentManager.TextSize, defaultBottomSpace, 1.0); | |
} | |
else | |
{ | |
lineTransform = new LineTransform(0, 0, 1.0); | |
} | |
return lineTransform; | |
} | |
} | |
} | |
"; | |
public List<(string path, Dictionary<string, string> xDoc)> FakeXmlDocs | |
{ | |
get | |
{ | |
var result = new List<(string path, Dictionary<string, string> xDoc)> | |
{ | |
("c:\\path\\to\\Resources.resx", | |
new Dictionary<string, string> | |
{ | |
{"Name1", "some value1"}, | |
{"Nam2", "some value2"}, | |
{"Name3", "some value3"}, | |
}), | |
("c:\\path\\to\\Resources1.resx", | |
new Dictionary<string, string> | |
{ | |
{"Name1", "some value1"}, | |
{"Nam2", "some value2"}, | |
{"Name3", "some value3"}, | |
{"CreatedMyLineTransformSource", "some value"}, | |
{"Info_TransformLine", "some value"}, | |
}), | |
("c:\\path\\to\\Resources2.resx", | |
new Dictionary<string, string> | |
{ | |
{"Name1", "some value1"}, | |
{"Nam2", "some value2"}, | |
{"Name3", "some value3"}, | |
}), | |
("c:\\path\\to\\other\\Resources.resx", | |
new Dictionary<string, string> | |
{ | |
{"Name1", "some other value1"}, | |
{"Nam2", "some other value2"}, | |
{"Name3", "some other value3"}, | |
}), | |
("c:\\path\\to\\other\\Resources1.resx", | |
new Dictionary<string, string> | |
{ | |
{"Name1", "some other value1"}, | |
{"Nam2", "some other value2"}, | |
{"Name3", "some other value3"}, | |
{"CreatedMyLineTransformSource", "some other value"}, | |
{"Info_TransformLine", "some other value"}, | |
}), | |
}; | |
return result; | |
} | |
////if (xmlDocs == null)////{//// try//// {//// xmlDocs = new List<(string, XmlDocument)>();//// foreach (var resourceFile in ResourceFiles)//// {//// var xdoc = new XmlDocument();//// xdoc.Load(resourceFile);//// xmlDocs.Add((resourceFile, xdoc));//// }//// }//// catch (Exception exc)//// {//// Debug.WriteLine(exc);//// }////}////return xmlDocs; | |
} | |
[Benchmark] | |
public void UsingString() | |
{ | |
var fileLines = FileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None); | |
var searchTexts = new string[] { "Resources.", "Resources1.", "Resurces2.", "Resources.", "Resources1." }; | |
for (int i = 0; i < fileLines.Length; i++) | |
{ | |
ProcessLineWithString(fileLines[i], searchTexts); | |
} | |
} | |
// Equivalent to the interesting bits of CreateVisuals | |
private void ProcessLineWithString(string lineText, string[] searchTexts) | |
{ | |
int matchIndex = lineText.IndexOfAny(searchTexts); | |
if (matchIndex >= 0) | |
{ | |
var endPos = lineText.IndexOfAny(new[] { ' ', '.', '"', '(', ')' }, lineText.IndexOf('.', matchIndex) + 1); | |
string foundText; | |
if (endPos > matchIndex) | |
{ | |
foundText = lineText.Substring(matchIndex, endPos - matchIndex); | |
} | |
else | |
{ | |
foundText = lineText.Substring(matchIndex); | |
} | |
string displayText = null; | |
var resourceName = foundText.Substring(foundText.IndexOf('.') + 1); | |
foreach (var (path, xDoc) in FakeXmlDocs) | |
{ | |
if (foundText.StartsWith($"{Path.GetFileNameWithoutExtension(path)}.")) | |
{ | |
foreach (var element in xDoc) | |
{ | |
if (element.Key == resourceName) | |
{ | |
displayText = element.Value; | |
break; | |
} | |
} | |
} | |
} | |
if (!string.IsNullOrWhiteSpace(displayText)) | |
{ | |
//Console.WriteLine($"Create TextBlock for '{displayText}'"); | |
} | |
} | |
} | |
[Benchmark] | |
public void UsingSpanChar() | |
{ | |
var fileLines = FileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None); | |
var searchTexts = new List<string> { "Resources.", "Resources1.", "Resurces2.", "Resources.", "Resources1." }; | |
for (int i = 0; i < fileLines.Length; i++) | |
{ | |
ProcessLineWithSpanChar(fileLines[i], searchTexts); | |
} | |
} | |
// This was created for benchmarking | |
// second parameter is still strings as can't use `ReadOnlySpan<char>` as a type parameter (i.e. `List<ReadOnlySpan<char>>` is invalid) | |
private void ProcessLineWithSpanChar(string lineText, List<string> searchTexts) | |
{ | |
// TODO Change here | |
int matchIndex = lineText.AsSpan().IndexOfAnySpan(searchTexts); | |
if (matchIndex >= 0) | |
{ | |
// TODO Change here | |
var endPos = lineText.AsSpan().IndexOfAnySpan(new[] { ' ', '.', '"', '(', ')' }, lineText.IndexOf('.', matchIndex) + 1); | |
ReadOnlySpan<char> foundText; | |
if (endPos > matchIndex) | |
{ | |
foundText = lineText.AsSpan().Slice(matchIndex, endPos - matchIndex); | |
} | |
else | |
{ | |
// TODO Change here | |
foundText = lineText.AsSpan().Slice(matchIndex); | |
} | |
string displayText = null; | |
// TODO Change here | |
var resourceName = foundText.Slice(foundText.IndexOf('.') + 1); | |
foreach (var (path, xDoc) in FakeXmlDocs) | |
{ | |
if (foundText.StartsWith($"{Path.GetFileNameWithoutExtension(path)}.".AsSpan())) | |
{ | |
foreach (var element in xDoc) | |
{ | |
if (element.Key.AsSpan() == resourceName) | |
{ | |
displayText = element.Value; | |
break; | |
} | |
} | |
} | |
} | |
// TODO Change here? | |
if (!string.IsNullOrWhiteSpace(displayText)) | |
{ | |
//Console.WriteLine($"Create TextBlock for '{displayText}'"); | |
} | |
} | |
} | |
} | |
public static class StringExtensions | |
{ | |
public static int IndexOfAny(this string source, params string[] values) | |
{ | |
try | |
{ | |
var valuePostions = new Dictionary<string, int>(); | |
// Values may be duplicated if multiple apps in the project have resources with the same name. | |
foreach (var value in values.Distinct()) | |
{ | |
valuePostions.Add(value, source.IndexOf(value)); | |
} | |
if (valuePostions.Any(v => v.Value > -1)) | |
{ | |
var result = valuePostions.Select(v => v.Value).Where(v => v > -1).OrderByDescending(v => v).FirstOrDefault(); | |
return result; | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(source); | |
Console.WriteLine(values); | |
Console.WriteLine(e); | |
} | |
return -1; | |
} | |
public static int IndexOfAnySpan(this ReadOnlySpan<char> source, List<string> values) | |
{ | |
return source.IndexOfAnySpan(values, 0, source.Length); | |
} | |
public static int IndexOfAnySpan(this ReadOnlySpan<char> source, List<string> values, int start) | |
{ | |
return source.IndexOfAnySpan(values, start, source.Length - start); | |
} | |
public static int IndexOfAnySpan(this ReadOnlySpan<char> source, List<string> values, int start, int length) | |
{ | |
try | |
{ | |
var valuePostions = new Dictionary<string, int>(); | |
// Values may be duplicated if multiple apps in the project have resources with the same name. | |
foreach (var value in values.ToArray().Distinct()) | |
{ | |
valuePostions.Add(value, source.Slice(start, length).IndexOf(value.AsSpan())); | |
} | |
if (valuePostions.Any(v => v.Value > -1)) | |
{ | |
var result = valuePostions.Select(v => v.Value).Where(v => v > -1).OrderByDescending(v => v).FirstOrDefault(); | |
return result; | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(source.ToString()); | |
Console.WriteLine(values); | |
Console.WriteLine(e); | |
} | |
return -1; | |
} | |
public static int IndexOfAnySpan(this ReadOnlySpan<char> source, char[] values, int start) | |
{ | |
return source.IndexOfAnySpan(values, start, source.Length - start); | |
} | |
public static int IndexOfAnySpan(this ReadOnlySpan<char> source, char[] values, int start, int length) | |
{ | |
try | |
{ | |
var valuePostions = new Dictionary<string, int>(); | |
// Values may be duplicated if multiple apps in the project have resources with the same name. | |
foreach (var value in values.ToArray().Distinct()) | |
{ | |
valuePostions.Add(value.ToString(), source.Slice(start, length).IndexOf(value)); | |
} | |
if (valuePostions.Any(v => v.Value > -1)) | |
{ | |
var result = valuePostions.Select(v => v.Value).Where(v => v > -1).OrderByDescending(v => v).FirstOrDefault(); | |
return result; | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(source.ToString()); | |
Console.WriteLine(values); | |
Console.WriteLine(e); | |
} | |
return -1; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment