Skip to content

Commit abc33b6

Browse files
committed
Implementations will run in a seperated thread now
Improved NaiveSIMD Fixed a few typos Renamed Trainer implementation to memory.dll Updated to .NET Framework 4.7.2
1 parent fa056b6 commit abc33b6

File tree

9 files changed

+137
-69
lines changed

9 files changed

+137
-69
lines changed

PatternScanBench/App.config

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<configuration>
33
<startup>
4-
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
55
</startup>
6+
<runtime>
7+
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
8+
<dependentAssembly>
9+
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
10+
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
11+
</dependentAssembly>
12+
</assemblyBinding>
13+
</runtime>
614
</configuration>

PatternScanBench/Implementations/PatternScanALittleBitNaiveFor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal override long FindPattern(in byte[] cbMemory, in byte[] cbPattern, stri
3737
int iy;
3838

3939
List<byte> not0PatternBytesList = new List<byte>();
40-
List<int> not0PatternBytesIndexIndexList = new List<int>();
40+
List<int> not0PatternBytesIndexList = new List<int>();
4141

4242
int dataLength = cbMemory.Length - cbPattern.Length;
4343

@@ -46,13 +46,13 @@ internal override long FindPattern(in byte[] cbMemory, in byte[] cbPattern, stri
4646
if (szMask[iy] == 'x')
4747
{
4848
not0PatternBytesList.Add(cbPattern[iy]);
49-
not0PatternBytesIndexIndexList.Add(iy);
49+
not0PatternBytesIndexList.Add(iy);
5050
}
5151
}
5252

5353
byte[] not0PatternBytesArray = not0PatternBytesList.ToArray();
5454
int not0PatternBytesL = not0PatternBytesArray.Length;
55-
int[] not0PatternBytesIndexArray = not0PatternBytesIndexIndexList.ToArray();
55+
int[] not0PatternBytesIndexArray = not0PatternBytesIndexList.ToArray();
5656

5757
for (ix = 0; ix < dataLength; ix++)
5858
{

PatternScanBench/Implementations/PatternScanLearnMore.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,17 @@ internal override long FindPattern(in byte[] cbMemory, in byte[] cbPattern, stri
6565

6666
return -1;
6767
}
68+
69+
private static bool IsMatch(in byte[] addr, in byte[] pat, in byte[] msk)
70+
{
71+
uint n = 0;
72+
while (addr[n] == pat[n] || msk[n] == (byte)'?') {
73+
if (n >= msk.Length) {
74+
return true;
75+
}
76+
}
77+
return false;
78+
}
79+
6880
}
6981
}

PatternScanBench/Implementations/PatternScanTrainer.cs renamed to PatternScanBench/Implementations/PatternScanMemoryDLL.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
namespace PatternScanBench.Implementations
33
{
44
/// <summary>
5-
/// Pattern scan implementation 'Trainer' - by erfg12
5+
/// Pattern scan implementation 'MemoryDLL' - by erfg12
66
/// https://github.com/erfg12
77
/// https://github.com/erfg12/memory.dll/blob/master/Memory/memory.cs
88
/// </summary>
9-
internal class PatternScanTrainer : PatternScanAlgorithm
9+
internal class PatternScanMemoryDLL : PatternScanAlgorithm
1010
{
1111
internal override string Creator => "erfg12";
12-
internal PatternScanTrainer() { }
12+
internal PatternScanMemoryDLL() { }
1313

1414
/// <summary>
15-
/// Initializes a new 'PatternScanTrainer'.
15+
/// Initializes a new 'PatternScanMemoryDLL'.
1616
/// </summary>
1717
/// <param name="cbMemory">The byte array to scan.</param>
1818
/// <returns>An optional string to display next to benchmark results.</returns>
@@ -22,7 +22,7 @@ internal override string Init(in byte[] cbMemory)
2222
}
2323

2424
/// <summary>
25-
/// Returns address of pattern using 'Trainer' implementation by erfg12. Can match 0.
25+
/// Returns address of pattern using 'MemoryDLL' implementation by erfg12. Can match 0.
2626
/// </summary>
2727
/// <param name="cbMemory">The byte array to scan.</param>
2828
/// <param name="cbPattern">The byte pattern to look for, wildcard positions are replaced by 0.</param>

PatternScanBench/Implementations/PatternScanNaiveSIMD.cs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,40 +44,46 @@ internal override string Init(in byte[] cbMemory)
4444
/// <returns>-1 if pattern is not found.</returns>
4545
internal override long FindPattern(in byte[] cbMemory, in byte[] cbPattern, string szMask)
4646
{
47+
byte[] cxMemory = cbMemory;
48+
byte[] cxPattern = cbPattern;
4749
int maskLength = szMask.Length;
4850
ushort[] matchTable = BuildMatchIndexes(szMask, maskLength);
4951
int matchTableLength = matchTable.Length;
50-
Vector<byte>[] patternVectors = PadPatternToVector(cbPattern);
52+
Vector<byte>[] patternVectors = PadPatternToVector(cxPattern);
5153

52-
int searchLength = cbMemory.Length - (_simdLength > cbPattern.Length ? _simdLength : cbPattern.Length);
54+
int searchLength = cxMemory.Length - (_simdLength > cxPattern.Length ? _simdLength : cxPattern.Length);
5355
for (int position = 0; position < searchLength; position++)
5456
{
55-
if (cbMemory[position] == cbPattern[0])
57+
if (cxPattern[0] != cxMemory[position])
5658
{
57-
int iMatchTableIndex = 0;
58-
bool found = true;
59-
for (int i = 0; i < patternVectors.Length; i++)
60-
{
61-
Vector<byte> compareResult = patternVectors[i] - new Vector<byte>(cbMemory, position + 1 + (i * _simdLength));
62-
for (; iMatchTableIndex < matchTableLength; iMatchTableIndex++)
63-
{
64-
int matchIndex = matchTable[iMatchTableIndex];
65-
if (i > 0) matchIndex -= i * _simdLength;
66-
if (matchIndex >= _simdLength)
67-
break;
68-
if (compareResult[matchIndex] == 0x00)
69-
continue;
70-
found = false;
71-
break;
72-
}
59+
if (cxPattern[0] != cxMemory[position+1])
60+
position++;
61+
continue;
62+
}
7363

74-
if (!found)
64+
int iMatchTableIndex = 0;
65+
bool found = true;
66+
for (int i = 0; i < patternVectors.Length; i++)
67+
{
68+
Vector<byte> compareResult = patternVectors[i] - new Vector<byte>(cxMemory, position + 1 + (i * _simdLength));
69+
for (; iMatchTableIndex < matchTableLength; iMatchTableIndex++)
70+
{
71+
int matchIndex = matchTable[iMatchTableIndex];
72+
if (i > 0) matchIndex -= i * _simdLength;
73+
if (matchIndex >= _simdLength)
7574
break;
75+
if (compareResult[matchIndex] == 0x00)
76+
continue;
77+
found = false;
78+
break;
7679
}
7780

78-
if (found)
79-
return position;
81+
if (!found)
82+
break;
8083
}
84+
85+
if (found)
86+
return position;
8187
}
8288

8389
return -1;

PatternScanBench/PatternScanBench.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<OutputType>Exe</OutputType>
99
<RootNamespace>PatternScanBench</RootNamespace>
1010
<AssemblyName>PatternScanBench</AssemblyName>
11-
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
11+
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
1212
<FileAlignment>512</FileAlignment>
1313
<Deterministic>true</Deterministic>
1414
<TargetFrameworkProfile />
@@ -36,6 +36,7 @@
3636
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
3737
<Prefer32Bit>true</Prefer32Bit>
3838
<LangVersion>7.2</LangVersion>
39+
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
3940
</PropertyGroup>
4041
<PropertyGroup />
4142
<PropertyGroup>
@@ -65,7 +66,7 @@
6566
<Compile Include="Implementations\PatternScanCompareByteArray.cs" />
6667
<Compile Include="Implementations\PatternScanNaiveSIMD.cs" />
6768
<Compile Include="Implementations\PatternScanLearnMore.cs" />
68-
<Compile Include="Implementations\PatternScanTrainer.cs" />
69+
<Compile Include="Implementations\PatternScanMemoryDLL.cs" />
6970
<Compile Include="Implementations\PatternScanTemplate.cs" />
7071
<Compile Include="Program.cs" />
7172
<Compile Include="Properties\AssemblyInfo.cs" />

PatternScanBench/Program.cs

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Collections.Generic;
77
using System.Security.Cryptography;
8+
using System.Threading.Tasks;
89
using PatternScanBench.Implementations;
910

1011
namespace PatternScanBench
@@ -16,13 +17,13 @@ Add your implementation here
1617
*/
1718
static readonly Dictionary<string, PatternScanAlgorithm> PATTERN_SCAN_ALGORITHMS = new Dictionary<string, PatternScanAlgorithm>
1819
{
19-
{ "ALittleBitNaiveFor", new PatternScanALittleBitNaiveFor() }, // by DI20ID
2020
{ "LearnMore", new PatternScanLearnMore() }, // by learn_more
21-
{ "Trainer", new PatternScanTrainer() }, // by erfg12
22-
{ "NaiveSIMD", new PatternScanNaiveSIMD() }, // by uberhalit
21+
{ "memory.dll", new PatternScanMemoryDLL() }, // by erfg12
2322
{ "CompareByteArray", new PatternScanCompareByteArray() }, // by fdsasdf
2423
{ "BytePointerWithJIT", new PatternScanBytePointerWithJIT() }, // by M i c h a e l
2524
{ "BoyerMooreHorspool", new PatternScanBoyerMooreHorspool() }, // by DarthTon
25+
{ "ALittleBitNaiveFor", new PatternScanALittleBitNaiveFor() }, // by DI20ID
26+
{ "NaiveSIMD", new PatternScanNaiveSIMD() }, // by uberhalit
2627
{ "NaiveFor", new PatternScanNaiveFor() }, // by uberhalit
2728

2829
#if (!DEBUG)
@@ -70,7 +71,7 @@ internal static void Main(string[] args)
7071
Console.WriteLine();
7172
Console.ForegroundColor = ConsoleColor.Gray;
7273

73-
PrintInfo(string.Format("{0} iterations | {1} patterns", ITERATIONS, TARGET_PATTERNS.Count));
74+
PrintInfo(string.Format("{0} iterations | {1} patterns | {2} implementations", ITERATIONS, TARGET_PATTERNS.Count, PATTERN_SCAN_ALGORITHMS.Count));
7475
Console.Write("To start press ENTER...");
7576
Console.ReadLine();
7677
Console.WriteLine("");
@@ -107,44 +108,74 @@ internal static void Main(string[] args)
107108
}
108109
GC.KeepAlive(memoryPatterns);
109110

111+
GC.KeepAlive(PATTERN_SCAN_ALGORITHMS);
112+
110113
// bench all algorithms
111-
Stopwatch stopWatch = new Stopwatch();
112114
foreach (KeyValuePair<string, PatternScanAlgorithm> patternScanAlgorithm in PATTERN_SCAN_ALGORITHMS)
113115
{
114-
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
115116
PrintInfo(patternScanAlgorithm.Key + " - by " + patternScanAlgorithm.Value.Creator);
116-
long[] results = new long[ITERATIONS];
117-
long lastRun = 0;
118-
bool algoSuccess = true;
119-
stopWatch.Restart();
120-
string message = patternScanAlgorithm.Value.Init(in moduleMemory);
121-
for (int run = 0; run < ITERATIONS; run++)
122-
{
123-
if (!stopWatch.IsRunning)
124-
stopWatch.Restart();
125-
foreach (MemoryPattern memoryPattern in memoryPatterns)
126-
{
127-
if (patternScanAlgorithm.Value.FindPattern(in moduleMemory, in memoryPattern.CbPattern, memoryPattern.SzMask) == memoryPattern.ExpectedAddress) continue;
128-
algoSuccess = false;
129-
break;
130-
}
131-
stopWatch.Stop();
132-
if (!algoSuccess)
133-
break;
134-
results[run] = stopWatch.ElapsedMilliseconds;
135-
}
136-
137-
if (!algoSuccess)
138-
PrintError("failed..." + (message != "" ? " (" + message + ")" : ""));
117+
Task<BenchmarkResult> benchMark = RunBenchmark(patternScanAlgorithm, memoryPatterns, moduleMemory);
118+
if (!benchMark.Wait(25000))
119+
PrintError("timeout...");
120+
else if (!benchMark.Result.success)
121+
PrintError("failed..." + (benchMark.Result.message != "" ? " (" + benchMark.Result.message + ")" : ""));
139122
else
140-
PrintResults(results, message);
123+
PrintResults(benchMark.Result.timings, benchMark.Result.message);
141124
}
142125

143126
Console.WriteLine("");
144127
Console.WriteLine("finished...");
145128
Console.ReadLine();
146129
}
147130

131+
/// <summary>
132+
/// Run a pattern scan benchmark.
133+
/// </summary>
134+
/// <param name="patternScanAlgorithm">The PatternScanAlgorithm to test.</param>
135+
/// <param name="memoryPatterns">A list of MemoryPatterns to look for.</param>
136+
/// <param name="moduleMemory">The memory to scan for the patterns.</param>
137+
/// <returns>A benchmark result.</returns>
138+
internal static async Task<BenchmarkResult> RunBenchmark(KeyValuePair<string, PatternScanAlgorithm> patternScanAlgorithm, List<MemoryPattern> memoryPatterns, byte[] moduleMemory)
139+
{
140+
await Task.Delay(100); // give thread some time to initialize
141+
BenchmarkResult benchmarkResult = new BenchmarkResult
142+
{
143+
success = false,
144+
timings = new long[ITERATIONS]
145+
};
146+
bool algoSuccess = true;
147+
148+
Stopwatch stopWatch = new Stopwatch();
149+
GC.KeepAlive(stopWatch);
150+
stopWatch.Start();
151+
benchmarkResult.message = patternScanAlgorithm.Value.Init(in moduleMemory);
152+
stopWatch.Stop();
153+
for (int run = 0; run < ITERATIONS; run++)
154+
{
155+
GC.Collect();
156+
if (GC.WaitForFullGCComplete() != GCNotificationStatus.Succeeded)
157+
await Task.Delay(100);
158+
GC.WaitForPendingFinalizers();
159+
if (run == 0)
160+
stopWatch.Start();
161+
else
162+
stopWatch.Restart();
163+
foreach (MemoryPattern memoryPattern in memoryPatterns)
164+
{
165+
if (patternScanAlgorithm.Value.FindPattern(in moduleMemory, in memoryPattern.CbPattern, memoryPattern.SzMask) == memoryPattern.ExpectedAddress) continue;
166+
algoSuccess = false;
167+
break;
168+
}
169+
stopWatch.Stop();
170+
if (!algoSuccess)
171+
return benchmarkResult;
172+
benchmarkResult.timings[run] = (long)(stopWatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0);
173+
}
174+
175+
benchmarkResult.success = true;
176+
return benchmarkResult;
177+
}
178+
148179
/// <summary>
149180
/// Prints an error message.
150181
/// </summary>
@@ -192,6 +223,16 @@ internal static void PrintResults(long[] results, string msg = "")
192223
}
193224
Console.WriteLine();
194225
}
226+
227+
/// <summary>
228+
/// A benchmark result.
229+
/// </summary>
230+
internal struct BenchmarkResult
231+
{
232+
internal long[] timings;
233+
internal string message;
234+
internal bool success;
235+
}
195236
}
196237

197238
internal class MemoryPattern

PatternScanBench/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[assembly: AssemblyConfiguration("")]
77
[assembly: AssemblyCompany("uberhalit")]
88
[assembly: AssemblyProduct("PatternScanBench")]
9-
[assembly: AssemblyCopyright("Copyright © uberhalit 2019")]
9+
[assembly: AssemblyCopyright("Copyright © uberhalit 2020")]
1010
[assembly: AssemblyTrademark("")]
1111
[assembly: AssemblyCulture("")]
1212

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22

33
A small and simple benchmark for pattern scan (findpattern) implementations written in C# inspired by [this great C++ comparison](https://github.com/learn-more/findpattern-bench). Aimed to compare real-world scanning performance without any frills.
44

5-
[![PatternScanBenchmark](https://camo.githubusercontent.com/b9e0518be0a1615d593dbc06300c6d6517ac862b/68747470733a2f2f692e696d6775722e636f6d2f483077615732772e706e67)](#)
5+
[![PatternScanBenchmark](https://camo.githubusercontent.com/c88ae59cc829b6a44b11f81358a272aeab2661f4/68747470733a2f2f692e696d6775722e636f6d2f6332766a7445442e706e67)](#)
66

77
## Usage
88

99
Copy [memorydump.dat](PatternScanBench/Memorydump/memorydump.dat) into the same folder as the compiled benchmark and run the application.
1010

1111
## Contributing
1212

13-
Feel free to create a pull request with your own patter scan implementation at any time. Use [PatternScanTemplate.cs](PatternScanBench/Implementations/PatternScanTemplate.cs) as a template for your one, afterwards add it as the first element of `PATTERN_SCAN_ALGORITHMS` in [Program.cs](PatternScanBench/Program.cs) and create a pull request. Alternatively you can also try to improve an already existing one.
13+
Feel free to create a pull request with your own patter scan implementation at any time. Use [PatternScanTemplate.cs](PatternScanBench/Implementations/PatternScanTemplate.cs) as a template for your one while **following the rules**. Afterwards add it as the first element of `PATTERN_SCAN_ALGORITHMS` in [Program.cs](PatternScanBench/Program.cs) and create a pull request. Alternatively you can also try to improve an already existing one.
1414

1515
## Prerequisites
1616

17-
* .NET Framework 4.6
17+
* .NET Framework 4.7.2
1818
* Windows 10 64bit
1919

2020
## Building
2121

22-
Use Visual Studio 2017 or newer to build
22+
Use Visual Studio 2019 or newer to build
2323

2424
## License
2525

0 commit comments

Comments
 (0)