using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Glass.Editing;
namespace Glass.PerformanceTests
{
///
/// Times an edit followed by undoing it, at sizes around the point where undo starts keeping pages of the text in
/// place of a copy of it, for each point worth considering. A threshold no edit reaches is the control, since it
/// keeps every edit as a copy.
///
public static class UndoBenchmark
{
public const string Argument = "--undo";
public const string Usage = "Usage: Glass.PerformanceTests --undo [text sizes in chars, comma separated]";
public static readonly int[] DefaultSizes = { 10000000 };
public static readonly int[] EditSizes = { 1000, 3000, 4096, 6000, 8192, 12000, 16384, 65536, 1000000 };
public const int ChunkSize = 1000000;
public const int MostCharsTheControlCopies = 200000000;
public sealed class Variant
{
public string Name;
public int Threshold;
}
public static readonly Variant[] Variants =
{
new Variant { Name = "copy (control)", Threshold = int.MaxValue },
new Variant { Name = "1 page", Threshold = PagedText.DefaultPageSize },
new Variant { Name = "2 pages", Threshold = PagedText.DefaultPageSize * 2 },
new Variant { Name = "4 pages", Threshold = PagedText.DefaultPageSize * 4 },
};
private static EditBuffer Loaded(int size)
{
var returnMe = new EditBuffer();
string chunk = TypingBenchmark.TextOfAbout(ChunkSize);
while (returnMe.Length < size)
{
returnMe.Replace(returnMe.Length, 0, chunk);
}
returnMe.ClearHistory();
return returnMe;
}
private static void MeasureOnce(string name, Action action)
{
GC.Collect();
long bytesBefore = GC.GetAllocatedBytesForCurrentThread();
long start = Stopwatch.GetTimestamp();
action();
double milliseconds = (Stopwatch.GetTimestamp() - start) * 1000.0 / Stopwatch.Frequency;
Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
" {0,-34} {1,10:F2} ms {2,9:F1} MB allocated", name, milliseconds,
(GC.GetAllocatedBytesForCurrentThread() - bytesBefore) / 1048576.0));
}
public static int Run(string[] args)
{
var sizes = new List(DefaultSizes);
if (args.Length > 2)
{
Console.Error.WriteLine(Usage);
return 1;
}
if (args.Length == 2)
{
sizes.Clear();
foreach (string part in args[1].Split(','))
{
if (!int.TryParse(part, NumberStyles.None, CultureInfo.InvariantCulture, out int size) || size < 1)
{
Console.Error.WriteLine(Usage);
return 1;
}
sizes.Add(size);
}
}
string bitness = Environment.Is64BitProcess ? "x64" : "x86";
Console.WriteLine($"Edit then undo, {bitness}, {PagedText.DefaultPageSize}-char pages.");
foreach (int size in sizes)
{
Console.WriteLine($"--- {size:N0} chars");
EditBuffer buffer = Loaded(size);
int middle = buffer.Length / 2;
foreach (Variant variant in Variants)
{
buffer.PagedStepThreshold = variant.Threshold;
Console.WriteLine(TypingBenchmark.Format(TypingBenchmark.Measure(
"typing, " + variant.Name, () => buffer.Replace(middle, 0, "x"))));
buffer.ClearHistory();
}
foreach (int editSize in EditSizes)
{
if (editSize > size / 4)
{
continue;
}
string pasted = TypingBenchmark.TextOfAbout(editSize);
foreach (Variant variant in Variants)
{
buffer.PagedStepThreshold = variant.Threshold;
Console.WriteLine(TypingBenchmark.Format(TypingBenchmark.Measure(
$"{editSize} del+undo, {variant.Name}", () =>
{
buffer.Replace(middle, editSize, "");
buffer.Undo();
})));
Console.WriteLine(TypingBenchmark.Format(TypingBenchmark.Measure(
$"{editSize} paste+undo, {variant.Name}", () =>
{
buffer.Replace(middle, 0, pasted);
buffer.Undo();
})));
buffer.ClearHistory();
}
}
foreach (Variant variant in Variants)
{
// The control copies the half it deletes, a gigabyte at the largest sizes.
if (variant.Threshold == int.MaxValue && size > MostCharsTheControlCopies)
{
continue;
}
buffer.PagedStepThreshold = variant.Threshold;
int half = buffer.Length / 2;
int quarter = buffer.Length / 4;
MeasureOnce("delete half, " + variant.Name, () => buffer.Replace(quarter, half, ""));
MeasureOnce(" undo, " + variant.Name, () => buffer.Undo());
MeasureOnce(" redo, " + variant.Name, () => buffer.Redo());
MeasureOnce(" undo, " + variant.Name, () => buffer.Undo());
buffer.ClearHistory();
}
}
return 0;
}
}
}