Skip to content

Instantly share code, notes, and snippets.

@leppie
Last active May 17, 2016 17:21
Show Gist options
  • Save leppie/ef0a2521de5a603010374a8acb848395 to your computer and use it in GitHub Desktop.
Save leppie/ef0a2521de5a603010374a8acb848395 to your computer and use it in GitHub Desktop.
just playing
Total time: 00:19:30 (1170.69 sec)

// * Summary *

BenchmarkDotNet-Dev=v0.9.4.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz, ProcessorCount=8
Frequency=3246781 ticks, Resolution=307.9974 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
JitModules=clrjit-v4.6.1080.0

Type=Program  Mode=Throughput  Runtime=Clr
LaunchCount=1
              Method |     Median |    StdDev | Scaled |                    Value |       Match

------------------------ |----------- |---------- |------- |------------------------- |------------ ContainsTokenMonty | 5.6613 ns | 0.0203 ns | 1.21 | Bar;blaat;foo | Bar ContainsTokenUnroll | 4.6847 ns | 0.0277 ns | 1.00 | Bar;blaat;foo | Bar ContainsTokenUnrollIf | 5.6570 ns | 0.2017 ns | 1.21 | Bar;blaat;foo | Bar ContainsTokenUnrollJump | 6.5737 ns | 0.0065 ns | 1.40 | Bar;blaat;foo | Bar ContainsTokenUnrollMore | 4.7142 ns | 0.0183 ns | 1.01 | Bar;blaat;foo | Bar ContainsTokenMonty | 6.8144 ns | 0.0059 ns | 1.25 | Bar;blaat;foo | Whatevereva ContainsTokenUnroll | 5.4467 ns | 0.0645 ns | 1.00 | Bar;blaat;foo | Whatevereva ContainsTokenUnrollIf | 5.4067 ns | 0.0108 ns | 0.99 | Bar;blaat;foo | Whatevereva ContainsTokenUnrollJump | 5.4214 ns | 0.0991 ns | 1.00 | Bar;blaat;foo | Whatevereva ContainsTokenUnrollMore | 5.1779 ns | 0.0091 ns | 0.95 | Bar;blaat;foo | Whatevereva ContainsTokenMonty | 8.9323 ns | 0.0090 ns | 1.35 | Foo;Bar | Bar ContainsTokenUnroll | 6.5941 ns | 0.0561 ns | 1.00 | Foo;Bar | Bar ContainsTokenUnrollIf | 7.9967 ns | 0.0431 ns | 1.21 | Foo;Bar | Bar ContainsTokenUnrollJump | 9.2182 ns | 0.1237 ns | 1.40 | Foo;Bar | Bar ContainsTokenUnrollMore | 6.3479 ns | 0.0416 ns | 0.96 | Foo;Bar | Bar ContainsTokenMonty | 3.7640 ns | 0.0298 ns | 1.34 | Foo;Bar | Whatevereva ContainsTokenUnroll | 2.8139 ns | 0.0086 ns | 1.00 | Foo;Bar | Whatevereva ContainsTokenUnrollIf | 3.0534 ns | 0.0041 ns | 1.09 | Foo;Bar | Whatevereva ContainsTokenUnrollJump | 3.2886 ns | 0.3588 ns | 1.17 | Foo;Bar | Whatevereva ContainsTokenUnrollMore | 2.8166 ns | 0.0057 ns | 1.00 | Foo;Bar | Whatevereva ContainsTokenMonty | 16.4510 ns | 0.0351 ns | 1.09 | Foo;FooBar;Whatevereva | Bar ContainsTokenUnroll | 15.1601 ns | 0.1051 ns | 1.00 | Foo;FooBar;Whatevereva | Bar ContainsTokenUnrollIf | 15.8775 ns | 0.2600 ns | 1.05 | Foo;FooBar;Whatevereva | Bar ContainsTokenUnrollJump | 17.0039 ns | 0.0481 ns | 1.12 | Foo;FooBar;Whatevereva | Bar ContainsTokenUnrollMore | 14.4267 ns | 0.2444 ns | 0.95 | Foo;FooBar;Whatevereva | Bar ContainsTokenMonty | 16.2180 ns | 0.0602 ns | 1.25 | Foo;FooBar;Whatevereva | Whatevereva ContainsTokenUnroll | 12.9974 ns | 0.0874 ns | 1.00 | Foo;FooBar;Whatevereva | Whatevereva ContainsTokenUnrollIf | 12.8370 ns | 0.1017 ns | 0.99 | Foo;FooBar;Whatevereva | Whatevereva ContainsTokenUnrollJump | 12.9690 ns | 0.1191 ns | 1.00 | Foo;FooBar;Whatevereva | Whatevereva ContainsTokenUnrollMore | 12.7149 ns | 0.1162 ns | 0.98 | Foo;FooBar;Whatevereva | Whatevereva ContainsTokenMonty | 17.2238 ns | 0.0485 ns | 1.07 | Whatevereva;FooBar;Blaat | Bar ContainsTokenUnroll | 16.0485 ns | 0.0426 ns | 1.00 | Whatevereva;FooBar;Blaat | Bar ContainsTokenUnrollIf | 16.9675 ns | 0.0768 ns | 1.06 | Whatevereva;FooBar;Blaat | Bar ContainsTokenUnrollJump | 18.3669 ns | 0.2782 ns | 1.14 | Whatevereva;FooBar;Blaat | Bar ContainsTokenUnrollMore | 15.7898 ns | 0.2020 ns | 0.98 | Whatevereva;FooBar;Blaat | Bar ContainsTokenMonty | 6.6162 ns | 0.1030 ns | 0.88 | Whatevereva;FooBar;Blaat | Whatevereva ContainsTokenUnroll | 7.5284 ns | 0.0346 ns | 1.00 | Whatevereva;FooBar;Blaat | Whatevereva ContainsTokenUnrollIf | 7.0399 ns | 0.0177 ns | 0.94 | Whatevereva;FooBar;Blaat | Whatevereva ContainsTokenUnrollJump | 7.5271 ns | 0.0207 ns | 1.00 | Whatevereva;FooBar;Blaat | Whatevereva ContainsTokenUnrollMore | 7.5237 ns | 0.0100 ns | 1.00 | Whatevereva;FooBar;Blaat | Whatevereva ContainsTokenMonty | 15.7707 ns | 0.1978 ns | 1.15 | blaat;Whatevereva;Bar | Bar ContainsTokenUnroll | 13.6775 ns | 0.1128 ns | 1.00 | blaat;Whatevereva;Bar | Bar ContainsTokenUnrollIf | 14.8087 ns | 0.1109 ns | 1.08 | blaat;Whatevereva;Bar | Bar ContainsTokenUnrollJump | 16.2656 ns | 0.0164 ns | 1.19 | blaat;Whatevereva;Bar | Bar ContainsTokenUnrollMore | 13.1829 ns | 0.0239 ns | 0.96 | blaat;Whatevereva;Bar | Bar ContainsTokenMonty | 9.9736 ns | 0.0335 ns | 0.94 | blaat;Whatevereva;Bar | Whatevereva ContainsTokenUnroll | 10.6082 ns | 0.0921 ns | 1.00 | blaat;Whatevereva;Bar | Whatevereva ContainsTokenUnrollIf | 10.1404 ns | 0.0936 ns | 0.96 | blaat;Whatevereva;Bar | Whatevereva ContainsTokenUnrollJump | 10.8283 ns | 0.0437 ns | 1.02 | blaat;Whatevereva;Bar | Whatevereva ContainsTokenUnrollMore | 10.5999 ns | 0.0231 ns | 1.00 | blaat;Whatevereva;Bar | Whatevereva ContainsTokenMonty | 8.8163 ns | 0.1169 ns | 1.24 | foo;Bar;Blaat | Bar ContainsTokenUnroll | 7.1013 ns | 0.1190 ns | 1.00 | foo;Bar;Blaat | Bar ContainsTokenUnrollIf | 8.2087 ns | 0.0565 ns | 1.16 | foo;Bar;Blaat | Bar ContainsTokenUnrollJump | 9.6211 ns | 0.0112 ns | 1.35 | foo;Bar;Blaat | Bar ContainsTokenUnrollMore | 6.5971 ns | 0.0949 ns | 0.93 | foo;Bar;Blaat | Bar ContainsTokenMonty | 6.8146 ns | 0.0173 ns | 1.26 | foo;Bar;Blaat | Whatevereva ContainsTokenUnroll | 5.4216 ns | 0.0113 ns | 1.00 | foo;Bar;Blaat | Whatevereva ContainsTokenUnrollIf | 5.4020 ns | 0.0171 ns | 1.00 | foo;Bar;Blaat | Whatevereva ContainsTokenUnrollJump | 5.4030 ns | 0.0080 ns | 1.00 | foo;Bar;Blaat | Whatevereva ContainsTokenUnrollMore | 5.1800 ns | 0.0509 ns | 0.96 | foo;Bar;Blaat | Whatevereva

using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnostics;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
[Config(typeof(Config))]
public class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Program>();
}
private class Config : ManualConfig
{
public Config()
{
Add(Job.Clr.WithLaunchCount(1));
Add(new GCDiagnoser());
}
}
[Params("Foo;Bar", "Foo;FooBar;Whatevereva", "Bar;blaat;foo", "blaat;Whatevereva;Bar", "foo;Bar;Blaat", "Whatevereva;FooBar;Blaat")]
public string Value { get; set; }
[Params("Bar", "Whatevereva")] // worst cases
public string Match { get; set; }
private static char[] delimeter = new[] { ';' };
[Benchmark(Baseline = true)]
public bool ContainsTokenUnroll()
{
return ContainsTokenUnroll(Value, Match);
}
[Benchmark]
public bool ContainsTokenUnrollMore()
{
return ContainsTokenUnrollMore(Value, Match);
}
[Benchmark]
public bool ContainsTokenUnrollJump()
{
return ContainsTokenUnrollJump(Value, Match);
}
[Benchmark]
public bool ContainsTokenUnrollIf()
{
return ContainsTokenUnrollIf(Value, Match);
}
[Benchmark]
public bool ContainsTokenMonty()
{
return ContainsTokenMonty(Value, Match);
}
public static bool ContainsTokenUnrollJump(string value, string token, char delimiter = ';')
{
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
var valueLength = value.Length;
var tokenLength = token.Length;
if (tokenLength > valueLength) return false;
unsafe
{
fixed (char* valuePtr = value, tokenPtr = token)
{
char currentChar;
var curValuePtr = valuePtr;
var endValuePtr = valuePtr + valueLength - tokenLength;
do
{
var curTokenPtr = tokenPtr;
var count = tokenLength;
// Match token against the current position in value
while (count >= 4)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
count -= 4;
}
switch (count)
{
case 3:
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
goto case 1;
case 2:
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
break;
case 1:
if (*curValuePtr != *curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr++;
break;
}
// Token has been matched, check if the _next_ char
// in value is a delimiter or the end of value
if (*curValuePtr == delimiter || *curValuePtr == '\0')
{
return true;
}
curValuePtr++;
advanceToNextDelimiter:
// Find the next delimiter in value or the end of the string
// Unroll the loop by 4
while (true)
{
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
}
} while (curValuePtr <= endValuePtr);
}
return false;
}
}
public static bool ContainsTokenUnrollIf(string value, string token, char delimiter = ';')
{
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
var valueLength = value.Length;
var tokenLength = token.Length;
if (tokenLength > valueLength) return false;
unsafe
{
fixed (char* valuePtr = value, tokenPtr = token)
{
char currentChar;
var curValuePtr = valuePtr;
var endValuePtr = valuePtr + valueLength - tokenLength;
do
{
var curTokenPtr = tokenPtr;
var count = tokenLength;
// Match token against the current position in value
while (count >= 4)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
count -= 4;
}
if (count == 3)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
curTokenPtr += 2;
if (*curValuePtr != *curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr++;
}
else if (count == 2)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr += 2;
}
else if (count == 1)
{
if (*curValuePtr != *curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr++;
}
// Token has been matched, check if the _next_ char
// in value is a delimiter or the end of value
if (*curValuePtr == delimiter || *curValuePtr == '\0')
{
return true;
}
curValuePtr++;
advanceToNextDelimiter:
// Find the next delimiter in value or the end of the string
// Unroll the loop by 4
while (true)
{
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
}
} while (curValuePtr <= endValuePtr);
}
return false;
}
}
public static bool ContainsTokenMonty(string value, string token, char delimiter = ';')
{
const int charsPerLong = sizeof(long) / sizeof(char);
const int charsPerInt = sizeof(int) / sizeof(char);
const int bytesPerChar = sizeof(char) / sizeof(byte);
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
var delimiterTwice = (delimiter << 16) | delimiter;
var valueLength = value.Length;
var tokenLength = token.Length;
if (tokenLength > valueLength) return false;
int tokenLongs;
bool tokenTrailingInt, tokenTrailingChar;
{
var remainingChars = tokenLength;
tokenLongs = remainingChars / charsPerLong;
tokenTrailingInt = (tokenLength & 0x02) != 0;
tokenTrailingChar = (tokenLength & 0x01) != 0;
}
var tokenByteLength = tokenLength * bytesPerChar;
unsafe
{
fixed (char* valuePtr = value, tokenPtr = token)
{
var curValuePtr = valuePtr;
var endValuePtr = valuePtr + valueLength;
while (true)
{
long* tokenLongPtr = (long*)tokenPtr;
{
for (var i = 0; i < tokenLongs; i++)
{
var tokenLong = *tokenLongPtr;
var valueLong = *((long*)curValuePtr);
if (tokenLong == valueLong)
{
tokenLongPtr++;
curValuePtr += charsPerLong;
continue;
}
else
{
goto advanceToNextDelimiter;
}
}
}
int* tokenIntPtr = (int*)tokenLongPtr;
if (tokenTrailingInt)
{
var tokenInt = *tokenIntPtr;
var valueInt = *((int*)curValuePtr);
if (tokenInt == valueInt)
{
tokenIntPtr++;
curValuePtr += charsPerInt;
}
else
{
goto advanceToNextDelimiter;
}
}
char* tokenCharPtr = (char*)tokenIntPtr;
if (tokenTrailingChar)
{
var tokenChar = *tokenCharPtr;
var valueChar = *curValuePtr;
if (tokenChar == valueChar)
{
tokenCharPtr++;
curValuePtr++;
}
else
{
goto advanceToNextDelimiter;
}
}
if (curValuePtr == endValuePtr || *curValuePtr == delimiter)
{
return true;
}
advanceToNextDelimiter:
while (true)
{
var curVal = *((int*)curValuePtr);
var masked = curVal ^ delimiterTwice;
var temp = masked & 0x7FFF7FFF;
temp = temp + 0x7FFF7FFF;
temp = (int)(temp & 0x80008000);
temp = temp | masked;
temp = temp | 0x7FFF7FFF;
temp = ~temp;
var neitherMatch = temp == 0;
if (neitherMatch)
{
curValuePtr += charsPerInt;
if (curValuePtr >= endValuePtr)
{
return false;
}
continue;
}
var top16 = temp & 0xFFFF0000;
if (top16 != 0)
{
curValuePtr += charsPerInt;
break;
}
var bottom16 = temp & 0x0000FFFF;
if (bottom16 != 0)
{
curValuePtr += 1;
}
}
var remainingBytesInValue = ((byte*)endValuePtr) - ((byte*)curValuePtr);
if (remainingBytesInValue < tokenByteLength)
{
return false;
}
}
}
}
}
public static bool ContainsTokenUnrollMore(string value, string token, char delimiter = ';')
{
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
var valueLength = value.Length;
var tokenLength = token.Length;
if (tokenLength > valueLength) return false;
unsafe
{
fixed (char* valuePtr = value, tokenPtr = token)
{
char currentChar;
var curValuePtr = valuePtr;
var endValuePtr = valuePtr + valueLength - tokenLength;
do
{
var curTokenPtr = tokenPtr;
var count = tokenLength;
// Match token against the current position in value
while (count >= 2)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
count -= 2;
curValuePtr += 2;
curTokenPtr += 2;
}
if (count == 1)
{
if (*curValuePtr != *curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr++;
}
// Token has been matched, check if the _next_ char
// in value is a delimiter or the end of value
if (*curValuePtr == delimiter || *curValuePtr == '\0')
{
return true;
}
curValuePtr++;
advanceToNextDelimiter:
// Find the next delimiter in value or the end of the string
// Unroll the loop by 4
while (true)
{
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
}
} while (curValuePtr <= endValuePtr);
}
return false;
}
}
public static bool ContainsTokenUnroll(string value, string token, char delimiter = ';')
{
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
var valueLength = value.Length;
var tokenLength = token.Length;
if (tokenLength > valueLength) return false;
unsafe
{
fixed (char* valuePtr = value, tokenPtr = token)
{
char currentChar;
var curValuePtr = valuePtr;
var endValuePtr = valuePtr + valueLength - tokenLength;
do
{
var curTokenPtr = tokenPtr;
var count = tokenLength;
// Match token against the current position in value
while (count >= 2)
{
if (*(int*)curValuePtr != *(int*)curTokenPtr)
{
goto advanceToNextDelimiter;
}
count -= 2;
curValuePtr += 2;
curTokenPtr += 2;
}
if (count == 1)
{
if (*curValuePtr != *curTokenPtr)
{
goto advanceToNextDelimiter;
}
curValuePtr++;
}
// Token has been matched, check if the _next_ char
// in value is a delimiter or the end of value
if (*curValuePtr == delimiter || *curValuePtr == '\0')
{
return true;
}
curValuePtr++;
advanceToNextDelimiter:
// Find the next delimiter in value or the end of the string
// Unroll the loop by 2
while (true)
{
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
currentChar = *curValuePtr;
curValuePtr++;
if (currentChar == delimiter || currentChar == '\0')
{
break;
}
}
} while (curValuePtr <= endValuePtr);
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment