Skip to content

Instantly share code, notes, and snippets.

@mrlacey
Created November 12, 2018 21:05
Show Gist options
  • Save mrlacey/fd68ddd93446e1c5282afa536fb5780f to your computer and use it in GitHub Desktop.
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>
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