Last active
August 29, 2015 14:21
-
-
Save sumtec/611680a87984269af0cd to your computer and use it in GitHub Desktop.
JeffreyZhao's gist. Check ExtremeTextWriterUtils's result.
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.Globalization; | |
using System.IO; | |
namespace TestStringFormatPerformance | |
{ | |
public class CorrectnessTester<T> where T : ITextWriterUtils, new() | |
{ | |
public void Test() | |
{ | |
Test(0); | |
Test(int.MinValue); | |
Test(int.MaxValue); | |
Test("1234567890", 1); | |
Test("-1234567890", 2); | |
} | |
private void Test(string test, int startFrom) | |
{ | |
for (var i = startFrom; i < test.Length; i++) | |
{ | |
Test(int.Parse(test.Substring(0, i))); | |
} | |
} | |
public void Test(int number) | |
{ | |
var correctString = number.ToString(CultureInfo.InvariantCulture); | |
var writer = new StringWriter(); | |
var writerAdapter = new TextWriterAdapter(writer); | |
var util = new T(); | |
util.Write(writerAdapter, number); | |
var utilString = writer.ToString(); | |
var correct = correctString == utilString; | |
Console.WriteLine( | |
"[" + typeof(T).Name + "] " + correctString + " is " + | |
(correct | |
? "correct" | |
: ("in correct (" + utilString + ")") | |
) | |
); | |
} | |
} | |
} |
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.Globalization; | |
using System.IO; | |
using System.Linq; | |
namespace TestStringFormatPerformance | |
{ | |
public class ExtremeTextWriterUtils : ITextWriterUtils | |
{ | |
private static readonly string[] _zeroPaddingStrings; | |
private static readonly string[] _nonZeroPaddingStrings; | |
private static readonly string _minString; | |
static ExtremeTextWriterUtils() | |
{ | |
_zeroPaddingStrings = Enumerable.Range(0, 10000).Select(i => i.ToString("0000", CultureInfo.InvariantCulture)).ToArray(); | |
_nonZeroPaddingStrings = Enumerable.Range(0, 10000).Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray(); | |
_minString = int.MinValue.ToString(CultureInfo.InvariantCulture); | |
} | |
[ThreadStatic] | |
private static char[] _buffer; | |
private static char[] GetBuffer() | |
{ | |
if (_buffer == null) | |
{ | |
_buffer = new char[11]; | |
_buffer[0] = '-'; | |
} | |
return _buffer; | |
} | |
private static void WriteCore(ITextWriter writer, int start, int value) | |
{ | |
var buffer = GetBuffer(); | |
var high = value / 100000000; | |
int offset; | |
if (high > 0) | |
{ | |
offset = CopyToBuffer(buffer, 1, _nonZeroPaddingStrings[high]); | |
value -= high * 100000000; | |
high = value / 10000; | |
offset = CopyToBuffer(buffer, offset, _zeroPaddingStrings[high]); | |
value -= high * 10000; | |
offset = CopyToBuffer(buffer, offset, _zeroPaddingStrings[value]); | |
} | |
else | |
{ | |
high = value / 10000; | |
if (high > 0) | |
{ | |
offset = CopyToBuffer(buffer, 1, _nonZeroPaddingStrings[high]); | |
value -= high * 10000; | |
offset = CopyToBuffer(buffer, offset, _zeroPaddingStrings[value]); | |
} | |
else | |
{ | |
offset = CopyToBuffer(buffer, 1, _nonZeroPaddingStrings[value]); | |
} | |
} | |
writer.Write(buffer, start, offset - start); | |
} | |
private static int CopyToBuffer(char[] buffer, int offset, string s) | |
{ | |
for (int i = 0; i < s.Length; i++) | |
{ | |
buffer[offset++] = s[i]; | |
} | |
return offset; | |
} | |
public void Write(ITextWriter writer, int value) | |
{ | |
if (value == int.MinValue) { writer.Write(_minString); return; } | |
if (value < 0) { WriteCore(writer, 0, -value); return; } | |
if (value <= 9999) | |
{ | |
writer.Write(_nonZeroPaddingStrings[value]); | |
return; | |
} | |
WriteCore(writer, 1, value); | |
} | |
} | |
} |
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; | |
namespace TestStringFormatPerformance | |
{ | |
public class HentaiTextWriterUtils : ITextWriterUtils | |
{ | |
private readonly static char[] Map = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; | |
public void Write(ITextWriter writer, int value) | |
{ | |
var i = value; | |
var result = new char[11]; | |
var p = 10; | |
var neg = value < 0; | |
do | |
{ | |
var q = 0; | |
i = Math.DivRem(i, 10, out q); | |
result[p--] = neg ? Map[-q] : Map[q]; | |
} while (i != 0); | |
if (neg) | |
result[p--] = '-'; | |
writer.Write(result, p + 1, 10 - p); | |
} | |
} | |
} |
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
namespace TestStringFormatPerformance | |
{ | |
public interface ITextWriter | |
{ | |
void Write(string text); | |
void Write(string text, int startIndex, int count); | |
void Write(char[] chars); | |
void Write(char[] chars, int startIndex, int count); | |
} | |
} |
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
namespace TestStringFormatPerformance | |
{ | |
public interface ITextWriterUtils | |
{ | |
void Write(ITextWriter writer, int value); | |
} | |
} |
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; | |
namespace TestStringFormatPerformance | |
{ | |
public static class MagicSequence | |
{ | |
static MagicSequence() | |
{ | |
//Initialize(); | |
} | |
public static void Initialize() | |
{ | |
var bits = GenerateBits(); | |
//Count(bits); | |
var context = new Context { Heading = 9, Normal = 9999, Reversed = 9999 }; | |
context.Builder.Append(99999); | |
_created++; | |
_remain--; | |
bits[99999] = false; | |
Stopwatch watch = new Stopwatch(); | |
watch.Start(); | |
while (GenerateOne(bits, context)) | |
{ | |
//if ((_created % 10000) == 0) | |
//{ | |
// watch.Stop(); | |
// Console.WriteLine("T:{0}, C:{1}, R:{2} (S:{3:0.000}s)", _total, _created, _remain, watch.Elapsed.TotalSeconds); | |
// watch.Start(); | |
//} | |
} | |
watch.Stop(); | |
Console.WriteLine("T:{0}, C:{1}, R:{2} (S:{3:0.000}s)", _total, _created, _remain, watch.Elapsed.TotalSeconds); | |
} | |
private const string TEMPLATE = @" | |
namespace TestStringFormatPerformance | |
{ | |
public class SmartCopyBase | |
{ | |
protected const string Sequence = ""{1}""; | |
protected static readonly int[] Pointers = | |
{2}; | |
} | |
}"; | |
private static bool GenerateOne(bool[] bits, Context context) | |
{ | |
for (int level = 10; level <= 100000; level *= 10) | |
{ | |
context.Heading = (context.Reversed / (level / 10)) % 10; | |
//if (TryNormalPart(bits, context, level)) return true; | |
if (TryMixPart(bits, context, level)) return true; | |
///if (TryReversePart(bits, context, level)) return true; | |
} | |
//Dump(bits); | |
//StringBuilder builder = new StringBuilder(","); | |
//for (int i = 0; i < bits.Length; i++) | |
//{ | |
// if (bits[i] == false) continue; | |
// builder.Append(i.ToString("00000") + ","); | |
//} | |
//String x = builder.ToString(); | |
//string n = context.Normal.ToString("0000"); | |
//string r = context.Reversed.ToString("0000"); | |
//for (int i = 1; i <= 4; i++) | |
//{ | |
// if (x.Contains("," + n.Substring(4 - i)) || | |
// x.Contains(r.Substring(0, i) + ",")) | |
// { | |
// Console.WriteLine("!" + n + "," + r + ":" + i); | |
// } | |
//} | |
//context.Heading.ToString() | |
//Count(bits); | |
//for (int i = 9, h = 90000; i >= 0; i--, h -= 10000) | |
//{ | |
// for (int j = 9990; j >= 0; j -= 10) | |
// { | |
// if (TryNormal(bits, context, 100000, h + j, h + j)) return true; | |
// for (int k = 9; k >= i; k--) | |
// { | |
// if (TryNormal(bits, context, 100000, h + j + k, h + j + k)) return true; | |
// } | |
// } | |
//} | |
//Console.WriteLine("H:{0}, N:{1}, R:{2}", context.Heading, context.Normal, context.Reversed); | |
//Dump(bits); | |
return false; | |
} | |
private static void Count(bool[] bits) | |
{ | |
Console.WriteLine(bits.Count(item => item)); | |
} | |
private static void Dump(bool[] bits) | |
{ | |
int cnt = 0; | |
//for (int i = 0, h = 0; i <= 9; i++, h += 10000) | |
//{ | |
// for (int j = 0; j <= 9990; j += 10) | |
// { | |
// if (bits[i + j]) { Console.Write((i + j) + ","); cnt++; if ((cnt & 7) == 0) Console.WriteLine(); } | |
// for (int k = i; k <= 9; k++) | |
// { | |
// if (bits[i + j + k]) { Console.Write((i + j + k) + ","); cnt++; if ((cnt & 7) == 0) Console.WriteLine(); } | |
// } | |
// } | |
//} | |
for (int i = 0; i < 100000; i++) | |
{ | |
if (bits[i]) { Console.Write((i) + ","); cnt++; if ((cnt & 7) == 0) Console.WriteLine(); } | |
} | |
Console.WriteLine(); | |
} | |
private static bool TryMixPart(bool[] bits, Context context, int level) | |
{ | |
var enumeratorNormal = TryNormalPartMix(bits, context, level).GetEnumerator(); | |
var normalHasNext = enumeratorNormal.MoveNext(); | |
var lastNormal = enumeratorNormal.Current; | |
var enumeratorReversed = TryReversePartMix(bits, context, level).GetEnumerator(); | |
var reversedHasNext = enumeratorReversed.MoveNext(); | |
var lastReversed = enumeratorReversed.Current; | |
while (normalHasNext || reversedHasNext) | |
{ | |
if (!normalHasNext) | |
{ | |
if (TryReverse(bits, context, level, lastReversed.Value, lastReversed.AppendingValue)) return true; | |
reversedHasNext = enumeratorReversed.MoveNext(); | |
lastReversed = enumeratorReversed.Current; | |
continue; | |
} | |
if (!reversedHasNext || lastNormal.Value >= lastReversed.Value) | |
{ | |
if (TryNormal(bits, context, level, lastNormal.Value, lastNormal.AppendingValue)) return true; | |
normalHasNext = enumeratorNormal.MoveNext(); | |
lastNormal = enumeratorNormal.Current; | |
if (reversedHasNext && lastNormal.Value == lastReversed.Value) | |
{ | |
reversedHasNext = enumeratorReversed.MoveNext(); | |
lastReversed = enumeratorReversed.Current; | |
} | |
continue; | |
} | |
if (TryReverse(bits, context, level, lastReversed.Value, lastReversed.AppendingValue)) return true; | |
reversedHasNext = enumeratorReversed.MoveNext(); | |
lastReversed = enumeratorReversed.Current; | |
} | |
return false; | |
} | |
private class MixTesting | |
{ | |
public int Value; | |
public int AppendingValue; | |
} | |
private static IEnumerable<MixTesting> TryReversePartMix(bool[] bits, Context context, int level) | |
{ | |
int power = (int)Math.Log10(level); | |
int midLevel = (int)Math.Pow(10, 5 - power); | |
int currentTail = context.Reversed * 10 / level; | |
for (int head = (context.Heading == 0 ? 9 : context.Heading) * 10000; head >= 0; head -= 10000) | |
{ | |
for (int mid = (level / 10 - 1) * midLevel; mid >= 0; mid -= midLevel) | |
{ | |
int h = head + mid; | |
yield return new MixTesting { Value = h + currentTail, AppendingValue = h }; | |
} | |
} | |
} | |
private static IEnumerable<MixTesting> TryNormalPartMix(bool[] bits, Context context, int level) | |
{ | |
int currentHead = (context.Normal * level) % 100000; | |
for (int mid = level - 10; mid >= 0; mid -= 10) | |
{ | |
for (int tail = 9; tail >= context.Heading; tail--) | |
{ | |
int t = mid + tail; | |
yield return new MixTesting { Value = currentHead + t, AppendingValue = t }; | |
} | |
yield return new MixTesting { Value = currentHead + mid, AppendingValue = mid }; | |
} | |
} | |
//private static bool TryReversePart(bool[] bits, Context context, int level) | |
//{ | |
// int power = (int)Math.Log10(level); | |
// int midLevel = (int)Math.Pow(10, 5 - power); | |
// int currentTail = context.Reversed % midLevel; | |
// for (int head = (context.Heading - 1) * 10000; head >= 0; head -= 10000) | |
// { | |
// for (int mid = (level / 10 - 1) * midLevel; mid >= 0; mid -= midLevel) | |
// { | |
// int h = head + mid; | |
// int v = h + currentTail; | |
// if (TryReverse(bits, context, level, v, h)) return true; | |
// } | |
// } | |
// return false; | |
//} | |
//private static bool TryNormalPart(bool[] bits, Context context, int level) | |
//{ | |
// if (context.Heading == 9) return false; | |
// int currentHead = (context.Normal * level) % 100000; | |
// for (int mid = level - 10; mid >= 0; mid -= 10) | |
// { | |
// for (int tail = 9; tail > context.Heading; tail--) | |
// { | |
// int t = mid + tail; | |
// int v = currentHead + t; | |
// if (TryNormal(bits, context, level, v, t)) return true; | |
// } | |
// if (TryNormal(bits, context, level, currentHead + mid, mid)) return true; | |
// } | |
// return false; | |
//} | |
private static bool TryNormal(bool[] bits, Context context, int level, int v, int t) | |
{ | |
if (bits[v] == false) return false; | |
bits[v] = false; | |
context.Builder.Append(t.ToString(level.ToString().Substring(1))); | |
context.Normal = v % 10000; | |
//context.Heading = context.Normal / 1000; | |
context.Reversed = Reverse(context.Normal); | |
_remain--; | |
_created++; | |
return true; | |
} | |
private static bool TryReverse(bool[] bits, Context context, int level, int v, int h) | |
{ | |
if (bits[v] == false) return false; | |
bits[v] = false; | |
context.Builder.Append(Reverse(h).ToString(level.ToString().Substring(1))); | |
context.Reversed = v / 10; | |
//context.Heading = context.Reversed % 10; | |
context.Normal = Reverse(context.Reversed); | |
_remain--; | |
_created++; | |
return true; | |
} | |
private static int Reverse(int value) | |
{ | |
int result = 0; | |
for (int i = 0; i < 4; i++) | |
{ | |
result *= 10; | |
//if (value == 0) continue; | |
int next = value / 10; | |
result += value - next * 10; | |
value = next; | |
} | |
return result; | |
} | |
private static bool[] GenerateBits() | |
{ | |
var bits = new bool[100000]; | |
for (int i = 0, h = 0; i <= 9; i++, h += 10000) | |
{ | |
for (int j = 0; j <= 9990; j += 10) | |
{ | |
if (i != 0) | |
{ | |
bits[h + j] = true; | |
_total++; | |
} | |
for (int k = i; k <= 9; k++) | |
{ | |
bits[h + j + k] = true; | |
_total++; | |
} | |
} | |
} | |
_remain = _total; | |
return bits; | |
} | |
private static int ToPointer(int currentPosition, bool reverse, int value) | |
{ | |
int size = value == 0 ? 1 : (int)Math.Log10(value) + 1; | |
if (reverse == false) currentPosition -= 5; | |
return ((int)(reverse ? 0x80000000 : 0)) | (currentPosition << 3) | size; | |
} | |
public static string Sequence { get; private set; } | |
private static int _total; | |
private static int _remain; | |
private static int _created; | |
/* | |
63157 -> [N:3157, R:7513] | |
3157 [H:3, V: 3157] 31577-9 : 31578 -> [N:1578(H:1,V:1578), R:8751 -> (H:1,V:8751) | |
7513 [H:3, V: 7513] 1-37513 : 27513 -> [N:1572(H:1,V:1572), R:2751 -> (H:1,V:2751) | |
*/ | |
private class Context | |
{ | |
//public readonly char[] Buffer = new char[98304]; | |
//private int Offset; | |
//public void Write(int value, int length) | |
//{ | |
// Buffer | |
//} | |
public StringBuilder Builder = new StringBuilder(200000); | |
public int[] Pointers = new int[100000]; | |
public int Heading; | |
public int Normal; | |
public int Reversed; | |
} | |
} | |
} |
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.Concurrent; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace TestStringFormatPerformance | |
{ | |
public class MTRSBTester<T> : MultiThreadRandomPerformanceTester<T> where T : ITextWriterUtils, new() | |
{ | |
public MTRSBTester(RandomSet randomSet) : base(randomSet) { } | |
private const int MAX_ROUND = 1000000; | |
private ConcurrentBag<StringBuilder> _stringBuilders = new ConcurrentBag<StringBuilder>(); | |
public override void Test(int round) | |
{ | |
GC.Collect(2, GCCollectionMode.Forced); | |
GC.WaitForFullGCComplete(); | |
for (int i = 0; i < Environment.ProcessorCount; i++) | |
{ | |
_stringBuilders.Add(new StringBuilder(800000)); | |
} | |
base.Test(round); | |
GC.Collect(2, GCCollectionMode.Forced); | |
GC.WaitForFullGCComplete(); | |
} | |
protected override string ExtraInfo | |
{ | |
get | |
{ | |
return ", sb, " + _randomSet.Name; | |
} | |
} | |
protected override void TestCore() | |
{ | |
StringBuilder stringBuilder; | |
_stringBuilders.TryTake(out stringBuilder); | |
var writer = new TextWriterAdapter(new StringWriter(stringBuilder)); | |
var util = new T(); | |
int end = _round / _threads.Length; | |
for (int i = 0; i < end;) | |
{ | |
util.Write(writer, _randomSet.Get(i)); | |
i++; | |
if ((i & 0xFFFF) == 0) stringBuilder.Length = 0; | |
} | |
} | |
} | |
} |
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.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Threading; | |
namespace TestStringFormatPerformance | |
{ | |
public class MultiThreadPerformanceTester<T> where T : ITextWriterUtils, new() | |
{ | |
private const int _testValue = -1234567890; | |
protected Thread[] _threads = new Thread[Environment.ProcessorCount]; | |
protected int _round; | |
protected virtual string ExtraInfo { get { return null; } } | |
public virtual void Test(int round) | |
{ | |
_round = round; | |
for (int i = 0; i < _threads.Length; i++) | |
{ | |
_threads[i] = new Thread(TestCore); | |
} | |
var watch = new Stopwatch(); | |
watch.Start(); | |
for (int i = 0; i < _threads.Length; i++) | |
{ | |
_threads[i].Start(); | |
} | |
for (int i = 0; i < _threads.Length; i++) | |
{ | |
_threads[i].Join(); | |
} | |
watch.Stop(); | |
Console.WriteLine((round / watch.Elapsed.TotalSeconds) + " op/s [" + typeof(T).Name + "] Multi thread" + ExtraInfo); | |
} | |
protected virtual void TestCore() | |
{ | |
var writer = new TextWriterAdapter(TextWriter.Null); | |
var util = new T(); | |
int end = _round / _threads.Length; | |
for (int i = 0; i < end; i++) | |
{ | |
util.Write(writer, _testValue); | |
} | |
} | |
} | |
} |
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.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
namespace TestStringFormatPerformance | |
{ | |
public class MultiThreadRandomPerformanceTester<T> : MultiThreadPerformanceTester<T> where T : ITextWriterUtils, new() | |
{ | |
public MultiThreadRandomPerformanceTester(RandomSet randomSet) | |
{ | |
_randomSet = randomSet; | |
} | |
protected readonly RandomSet _randomSet; | |
protected override void TestCore() | |
{ | |
var writer = new TextWriterAdapter(TextWriter.Null); | |
var util = new T(); | |
int end = _round / _threads.Length; | |
for (int i = 0; i < end ; i++) | |
{ | |
util.Write(writer, _randomSet.Get(i)); | |
} | |
} | |
protected override string ExtraInfo | |
{ | |
get | |
{ | |
return ", " + _randomSet.Name ; | |
} | |
} | |
} | |
} |
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; | |
namespace TestStringFormatPerformance | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
new CorrectnessTester<SampletTextWriterUtils>().Test(); | |
new CorrectnessTester<ThreadStaticTextWriterUtils>().Test(); | |
new CorrectnessTester<HentaiTextWriterUtils>().Test(); | |
new CorrectnessTester<ExtremeTextWriterUtils>().Test(); | |
int round = 10000000; | |
new SingleThreadPerformanceTester<SampletTextWriterUtils>().Test(round); | |
new SingleThreadPerformanceTester<ThreadStaticTextWriterUtils>().Test(round); | |
new SingleThreadPerformanceTester<HentaiTextWriterUtils>().Test(round); | |
new SingleThreadPerformanceTester<ExtremeTextWriterUtils>().Test(round); | |
Console.WriteLine("-------\n"); | |
new MultiThreadPerformanceTester<SampletTextWriterUtils>().Test(round); | |
new MultiThreadPerformanceTester<ThreadStaticTextWriterUtils>().Test(round); | |
new MultiThreadPerformanceTester<HentaiTextWriterUtils>().Test(round); | |
new MultiThreadPerformanceTester<ExtremeTextWriterUtils>().Test(round); | |
Console.WriteLine("-------\n"); | |
Test(round, RandomSet.SmallNumbers); | |
Test(round, RandomSet.LargeNumbers); | |
Console.WriteLine("DONE!"); | |
Console.ReadKey(); | |
} | |
private static void Test(int round, RandomSet randomSet) | |
{ | |
new SingleThreadRandomPerformanceTester<SampletTextWriterUtils>().Test(round, randomSet); | |
new SingleThreadRandomPerformanceTester<ThreadStaticTextWriterUtils>().Test(round, randomSet); | |
new SingleThreadRandomPerformanceTester<HentaiTextWriterUtils>().Test(round, randomSet); | |
new SingleThreadRandomPerformanceTester<ExtremeTextWriterUtils>().Test(round, randomSet); | |
Console.WriteLine("-------\n"); | |
new MultiThreadRandomPerformanceTester<SampletTextWriterUtils>(randomSet).Test(round); | |
new MultiThreadRandomPerformanceTester<ThreadStaticTextWriterUtils>(randomSet).Test(round); | |
new MultiThreadRandomPerformanceTester<HentaiTextWriterUtils>(randomSet).Test(round); | |
new MultiThreadRandomPerformanceTester<ExtremeTextWriterUtils>(randomSet).Test(round); | |
Console.WriteLine("-------\n"); | |
new MTRSBTester<SampletTextWriterUtils>(randomSet).Test(round); | |
new MTRSBTester<ThreadStaticTextWriterUtils>(randomSet).Test(round); | |
new MTRSBTester<HentaiTextWriterUtils>(randomSet).Test(round); | |
new MTRSBTester<ExtremeTextWriterUtils>(randomSet).Test(round); | |
Console.WriteLine("-------\n"); | |
} | |
} | |
} |
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.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace TestStringFormatPerformance | |
{ | |
public class RandomSet | |
{ | |
static RandomSet() | |
{ | |
LargeNumbers = new RandomSet(int.MinValue, int.MaxValue, "largeRnd"); | |
SmallNumbers = new RandomSet(-200000, 200000, "smallRnd"); | |
} | |
public RandomSet(int min, int max, string name) | |
{ | |
Name = name; | |
var rnd = new Random(); | |
_numbers = Enumerable.Repeat(0, 2<<20).Select(i => rnd.Next(min, max)).ToArray(); | |
} | |
private readonly int[] _numbers; | |
public int Get(int i) | |
{ | |
return _numbers[i & 0xFFFFF]; | |
} | |
public static RandomSet LargeNumbers{get;private set;} | |
public static RandomSet SmallNumbers { get; private set; } | |
public string Name { get; private set; } | |
} | |
} |
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
[SampletTextWriterUtils] 0 is correct | |
[SampletTextWriterUtils] -2147483648 is correct | |
[SampletTextWriterUtils] 2147483647 is correct | |
[SampletTextWriterUtils] 1 is correct | |
[SampletTextWriterUtils] 12 is correct | |
[SampletTextWriterUtils] 123 is correct | |
[SampletTextWriterUtils] 1234 is correct | |
[SampletTextWriterUtils] 12345 is correct | |
[SampletTextWriterUtils] 123456 is correct | |
[SampletTextWriterUtils] 1234567 is correct | |
[SampletTextWriterUtils] 12345678 is correct | |
[SampletTextWriterUtils] 123456789 is correct | |
[SampletTextWriterUtils] -1 is correct | |
[SampletTextWriterUtils] -12 is correct | |
[SampletTextWriterUtils] -123 is correct | |
[SampletTextWriterUtils] -1234 is correct | |
[SampletTextWriterUtils] -12345 is correct | |
[SampletTextWriterUtils] -123456 is correct | |
[SampletTextWriterUtils] -1234567 is correct | |
[SampletTextWriterUtils] -12345678 is correct | |
[SampletTextWriterUtils] -123456789 is correct | |
[ThreadStaticTextWriterUtils] 0 is correct | |
[ThreadStaticTextWriterUtils] -2147483648 is correct | |
[ThreadStaticTextWriterUtils] 2147483647 is correct | |
[ThreadStaticTextWriterUtils] 1 is correct | |
[ThreadStaticTextWriterUtils] 12 is correct | |
[ThreadStaticTextWriterUtils] 123 is correct | |
[ThreadStaticTextWriterUtils] 1234 is correct | |
[ThreadStaticTextWriterUtils] 12345 is correct | |
[ThreadStaticTextWriterUtils] 123456 is correct | |
[ThreadStaticTextWriterUtils] 1234567 is correct | |
[ThreadStaticTextWriterUtils] 12345678 is correct | |
[ThreadStaticTextWriterUtils] 123456789 is correct | |
[ThreadStaticTextWriterUtils] -1 is correct | |
[ThreadStaticTextWriterUtils] -12 is correct | |
[ThreadStaticTextWriterUtils] -123 is correct | |
[ThreadStaticTextWriterUtils] -1234 is correct | |
[ThreadStaticTextWriterUtils] -12345 is correct | |
[ThreadStaticTextWriterUtils] -123456 is correct | |
[ThreadStaticTextWriterUtils] -1234567 is correct | |
[ThreadStaticTextWriterUtils] -12345678 is correct | |
[ThreadStaticTextWriterUtils] -123456789 is correct | |
[HentaiTextWriterUtils] 0 is correct | |
[HentaiTextWriterUtils] -2147483648 is correct | |
[HentaiTextWriterUtils] 2147483647 is correct | |
[HentaiTextWriterUtils] 1 is correct | |
[HentaiTextWriterUtils] 12 is correct | |
[HentaiTextWriterUtils] 123 is correct | |
[HentaiTextWriterUtils] 1234 is correct | |
[HentaiTextWriterUtils] 12345 is correct | |
[HentaiTextWriterUtils] 123456 is correct | |
[HentaiTextWriterUtils] 1234567 is correct | |
[HentaiTextWriterUtils] 12345678 is correct | |
[HentaiTextWriterUtils] 123456789 is correct | |
[HentaiTextWriterUtils] -1 is correct | |
[HentaiTextWriterUtils] -12 is correct | |
[HentaiTextWriterUtils] -123 is correct | |
[HentaiTextWriterUtils] -1234 is correct | |
[HentaiTextWriterUtils] -12345 is correct | |
[HentaiTextWriterUtils] -123456 is correct | |
[HentaiTextWriterUtils] -1234567 is correct | |
[HentaiTextWriterUtils] -12345678 is correct | |
[HentaiTextWriterUtils] -123456789 is correct | |
[ExtremeTextWriterUtils] 0 is correct | |
[ExtremeTextWriterUtils] -2147483648 is correct | |
[ExtremeTextWriterUtils] 2147483647 is correct | |
[ExtremeTextWriterUtils] 1 is correct | |
[ExtremeTextWriterUtils] 12 is correct | |
[ExtremeTextWriterUtils] 123 is correct | |
[ExtremeTextWriterUtils] 1234 is correct | |
[ExtremeTextWriterUtils] 12345 is correct | |
[ExtremeTextWriterUtils] 123456 is correct | |
[ExtremeTextWriterUtils] 1234567 is correct | |
[ExtremeTextWriterUtils] 12345678 is correct | |
[ExtremeTextWriterUtils] 123456789 is correct | |
[ExtremeTextWriterUtils] -1 is correct | |
[ExtremeTextWriterUtils] -12 is correct | |
[ExtremeTextWriterUtils] -123 is correct | |
[ExtremeTextWriterUtils] -1234 is correct | |
[ExtremeTextWriterUtils] -12345 is correct | |
[ExtremeTextWriterUtils] -123456 is correct | |
[ExtremeTextWriterUtils] -1234567 is correct | |
[ExtremeTextWriterUtils] -12345678 is correct | |
[ExtremeTextWriterUtils] -123456789 is correct | |
6605147.64255345 op/s [SampletTextWriterUtils] Single thread | |
8903163.23157404 op/s [ThreadStaticTextWriterUtils] Single thread | |
8716228.53672304 op/s [HentaiTextWriterUtils] Single thread | |
19648805.1168632 op/s [ExtremeTextWriterUtils] Single thread | |
------- | |
30720173.0160144 op/s [SampletTextWriterUtils] Multi thread | |
47360885.686451 op/s [ThreadStaticTextWriterUtils] Multi thread | |
44341080.4591075 op/s [HentaiTextWriterUtils] Multi thread | |
80020869.4427507 op/s [ExtremeTextWriterUtils] Multi thread | |
------- | |
7904069.88462429 op/s [SampletTextWriterUtils] Single thread, smallRnd | |
12246765.9658945 op/s [ThreadStaticTextWriterUtils] Single thread, smallRnd | |
13137032.7830712 op/s [HentaiTextWriterUtils] Single thread, smallRnd | |
17222609.6352579 op/s [ExtremeTextWriterUtils] Single thread, smallRnd | |
------- | |
33681850.6694941 op/s [SampletTextWriterUtils] Multi thread, smallRnd | |
63245346.4074113 op/s [ThreadStaticTextWriterUtils] Multi thread, smallRnd | |
60807055.5642712 op/s [HentaiTextWriterUtils] Multi thread, smallRnd | |
83077178.6990114 op/s [ExtremeTextWriterUtils] Multi thread, smallRnd | |
------- | |
27854183.3501619 op/s [SampletTextWriterUtils] Multi thread, sb, smallRnd | |
40513221.4898332 op/s [ThreadStaticTextWriterUtils] Multi thread, sb, smallRnd | |
37577018.7971521 op/s [HentaiTextWriterUtils] Multi thread, sb, smallRnd | |
47022289.9761174 op/s [ExtremeTextWriterUtils] Multi thread, sb, smallRnd | |
------- | |
6187943.818668 op/s [SampletTextWriterUtils] Single thread, largeRnd | |
7710310.08014328 op/s [ThreadStaticTextWriterUtils] Single thread, largeRnd | |
8233501.44222128 op/s [HentaiTextWriterUtils] Single thread, largeRnd | |
13942371.4384386 op/s [ExtremeTextWriterUtils] Single thread, largeRnd | |
------- | |
29246342.2330928 op/s [SampletTextWriterUtils] Multi thread, largeRnd | |
45563774.2479016 op/s [ThreadStaticTextWriterUtils] Multi thread, largeRnd | |
43212111.8364024 op/s [HentaiTextWriterUtils] Multi thread, largeRnd | |
62966661.6713115 op/s [ExtremeTextWriterUtils] Multi thread, largeRnd | |
------- | |
23357969.762641 op/s [SampletTextWriterUtils] Multi thread, sb, largeRnd | |
32218197.0954651 op/s [ThreadStaticTextWriterUtils] Multi thread, sb, largeRnd | |
30974971.2939454 op/s [HentaiTextWriterUtils] Multi thread, sb, largeRnd | |
40769267.1479615 op/s [ExtremeTextWriterUtils] Multi thread, sb, largeRnd | |
------- | |
DONE! |
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.Globalization; | |
namespace TestStringFormatPerformance | |
{ | |
public class SampletTextWriterUtils : ITextWriterUtils | |
{ | |
public void Write(ITextWriter writer, int value) | |
{ | |
writer.Write(value.ToString(CultureInfo.InvariantCulture)); | |
} | |
} | |
} |
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.Diagnostics; | |
using System.IO; | |
namespace TestStringFormatPerformance | |
{ | |
public class SingleThreadPerformanceTester<T> where T : ITextWriterUtils, new() | |
{ | |
private const int _testValue = -1234567890; | |
public void Test(int round) | |
{ | |
var writer = new TextWriterAdapter(TextWriter.Null); | |
var util = new T(); | |
// Cold start; | |
util.Write(writer, _testValue); | |
var watch = new Stopwatch(); | |
watch.Start(); | |
for (int i = 0; i < round; i++) | |
{ | |
util.Write(writer, _testValue); | |
} | |
watch.Stop(); | |
Console.WriteLine((round / watch.Elapsed.TotalSeconds) + " op/s [" + typeof(T).Name + "] Single thread"); | |
} | |
} | |
} |
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; | |
namespace TestStringFormatPerformance | |
{ | |
public class SingleThreadRandomPerformanceTester<T> where T : ITextWriterUtils, new() | |
{ | |
public void Test(int round, RandomSet randomSet) | |
{ | |
var writer = new TextWriterAdapter(TextWriter.Null); | |
var util = new T(); | |
// Cold start; | |
util.Write(writer, 0); | |
var watch = new Stopwatch(); | |
watch.Start(); | |
for (int i = 0; i < round; i++) | |
{ | |
util.Write(writer, randomSet.Get(i)); | |
} | |
watch.Stop(); | |
Console.WriteLine((round / watch.Elapsed.TotalSeconds) + " op/s [" + typeof(T).Name + "] Single thread, " + randomSet.Name); | |
} | |
} | |
} |
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.IO; | |
namespace TestStringFormatPerformance | |
{ | |
public class TextWriterAdapter : ITextWriter | |
{ | |
public TextWriterAdapter(TextWriter writer) | |
{ | |
_writer = writer; | |
} | |
private TextWriter _writer; | |
public void Write(char[] chars) | |
{ | |
_writer.Write(chars); | |
} | |
public void Write(string text) | |
{ | |
_writer.Write(text); | |
} | |
public void Write(char[] chars, int startIndex, int count) | |
{ | |
_writer.Write(chars, startIndex, count); | |
} | |
public void Write(string text, int startIndex, int count) | |
{ | |
_writer.Write(text.Substring(startIndex, count)); | |
} | |
} | |
} |
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.Globalization; | |
namespace TestStringFormatPerformance | |
{ | |
public class ThreadStaticTextWriterUtils :ITextWriterUtils | |
{ | |
[ThreadStatic] | |
private static char[] _buffer; | |
private static readonly string _intMinString = int.MinValue.ToString(CultureInfo.InvariantCulture); | |
public void Write(ITextWriter writer, int value) | |
{ | |
char[] buffer = GetBuffer(); | |
if (value == 0) { writer.Write("0"); return; } | |
if (value == int.MinValue) { writer.Write(_intMinString); return; } | |
bool negative = value < 0; | |
if (negative) value = -value; | |
int i = 11; | |
while (value != 0) | |
{ | |
i--; | |
buffer[i] = (char)((value % 10) + '0'); | |
value /= 10; | |
} | |
if (negative) | |
{ | |
buffer[--i] = '-'; | |
} | |
writer.Write(buffer, i, 11 - i); | |
} | |
private char[] GetBuffer() | |
{ | |
if (_buffer == null) | |
{ | |
_buffer = new char[11]; | |
} | |
return _buffer; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment