├── .github └── FUNDING.yml ├── .gitignore ├── DoDSamples.sln ├── DoDSamples ├── DoDSamples.csproj ├── Program.cs └── Samples │ ├── AOSvsSOA.cs │ ├── ArrayEnumerableByRef.cs │ ├── Benchmarks │ ├── InstructionAndDataDependency.cs │ ├── OOPvsDoDParsers.cs │ ├── RefListvsList.cs │ └── RowsVsCols.cs │ ├── BitOperations.cs │ ├── BlockList.cs │ ├── CacheSize.cs │ ├── InstructionLevelDependency.cs │ ├── MemoryModelDeadlock.cs │ ├── POParser │ ├── DoDPOParser_v1.cs │ ├── DoDPOParser_v1a.cs │ ├── DoDPOParser_v2.cs │ ├── DoDPOParser_v2a.cs │ ├── DoDPOParser_v3.cs │ ├── DoDPOParser_v3a.cs │ ├── DoDPOParser_v4.cs │ ├── DoDPOParser_v5.cs │ ├── OOPParser_Fast.cs │ ├── OOPParser_Slow.cs │ ├── POGenerator.cs │ └── Sample.po │ ├── RefList.cs │ ├── ReferenceHelpers.cs │ ├── SIMD.cs │ ├── SOAwithSIMD.cs │ ├── Tests │ ├── PoParserTests.cs │ └── RowsVsColsTests.cs │ └── Utils.cs ├── LICENSE └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ['https://www.buymeacoffee.com/LevelUp'] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /DoDSamples/bin 6 | /DoDSamples/obj 7 | -------------------------------------------------------------------------------- /DoDSamples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoDSamples", "DoDSamples\DoDSamples.csproj", "{3EA7FF30-3668-40EE-98A3-AF97EBBAF2EE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3EA7FF30-3668-40EE-98A3-AF97EBBAF2EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3EA7FF30-3668-40EE-98A3-AF97EBBAF2EE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3EA7FF30-3668-40EE-98A3-AF97EBBAF2EE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3EA7FF30-3668-40EE-98A3-AF97EBBAF2EE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {C2EF3999-A95E-4F75-93AA-E9BC82B5785F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DoDSamples/DoDSamples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | false 10 | true 11 | x64 12 | 13 | 14 | 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Always 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /DoDSamples/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using DoDSamples.Samples; 3 | using DoDSamples.Samples.Benchmarks; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.Intrinsics; 7 | using System.Runtime.Intrinsics.X86; 8 | 9 | namespace DoDSamples 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | 16 | 17 | 18 | ///////////////// 19 | // Rows vs Cols. 20 | ///////////////// 21 | 22 | // 23 | // Columns Array Traversal. 24 | // 25 | //RowsVsColsTests.Cols(); 26 | // 27 | // Rows Array Traversal. 28 | // 29 | //RowsVsColsTests.Rows(); 30 | 31 | ///////////////////////////////////// 32 | // PO File Parser - Sum the Amount. 33 | ///////////////////////////////////// 34 | 35 | // 36 | // Regex parser. 37 | // 38 | //POParserTests.TestAmountPO_Regex(); 39 | // 40 | // State Machine OOP Parser. 41 | // 42 | //POParserTests.TestAmountPO_OOP_StateMachine(); 43 | // 44 | // State Machine DoD Parser. 45 | // 46 | //POParserTests.TestAmountPO_DoD_StateMachine(); 47 | // 48 | // State Machine DoD Parser using split. 49 | // 50 | //POParserTests.TestAmountPO_DoD_StateMachine_Split(); 51 | // 52 | // State Machine DoD Parser using hot/cold split. 53 | // 54 | //POParserTests.TestAmountPO_DoD_SateMachine_HotColdSplit(); 55 | // 56 | // Fixed DoD Parsers 57 | // 58 | //POParserTests.TestAmountPO_DoD_StateMachineFix(); 59 | //POParserTests.TestAmountPO_DoD_SateMachine_SplitFix(); 60 | //POParserTests.TestAmountPO_DoD_SateMachine_HotColdSplitFix(); 61 | 62 | // 63 | // DoD SOA 64 | // 65 | //POParserTests.TestAmountPO_DoD_SoA(); 66 | //POParserTests.TestAmountPO_DoD_SoA_SIMD(); 67 | 68 | 69 | ////////////////////////////////////////////// 70 | // PO File Parser - Notes Split And To Lower. 71 | ////////////////////////////////////////////// 72 | 73 | // 74 | // State Machine OOP Split 75 | // 76 | //POParserTests.TestNotesPO_OOP_Split(); 77 | // 78 | // State Machine DoD Split 79 | // 80 | //POParserTests.TestNotesPO_DoD_Split(); 81 | 82 | // 83 | // State Machine OOP ToLower 84 | // 85 | //POParserTests.TestNotesToLowerPO_OOP(); 86 | // 87 | // State Machine DoD ToLower 88 | // 89 | //POParserTests.TestNotesToLowerPO_DoD(); 90 | 91 | 92 | //var summary = BenchmarkRunner.Run(); 93 | //var summary = BenchmarkRunner.Run(); 94 | //var summary = BenchmarkRunner.Run(); 95 | var summary = BenchmarkRunner.Run(); 96 | 97 | 98 | //SIMDSum simd = new SIMDSum(); 99 | 100 | // Utils.Measure(() => simd.Sum(new int[128*1024*1024])); 101 | // Utils.Measure(() => simd.SumVectorizedSse2(array)); 102 | 103 | 104 | //MemoryModelDeadlock memoryModelDeadlock = new MemoryModelDeadlock(); 105 | //Utils.Measure(memoryModelDeadlock.SpinLockTest); 106 | 107 | //AOSvsSOA soa = new AOSvsSOA(); 108 | //Utils.Measure(soa.TestSOA); 109 | 110 | //CacheSize c = new CacheSize(); 111 | 112 | //InstructionLevelDependency ld = new InstructionLevelDependency(); 113 | //Utils.Measure(ld.TestInDependant); 114 | 115 | //int steps = 65536 * 1024; 116 | //int[] array = new int[1024 * 1024 * 1]; 117 | 118 | //var e = Utils.Measure(() => c.TestK(steps, array)); 119 | //e = e * 1000; 120 | //Console.WriteLine((double)e / (double)steps); 121 | 122 | //var n = new POGenerator().Generate(); 123 | 124 | //RowsVsCols test = new RowsVsCols(); 125 | //Utils.Measure(test.Cols); 126 | //Console.ReadKey(); 127 | } 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /DoDSamples/Samples/AOSvsSOA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace DoDSamples 7 | { 8 | public class AOSvsSOA 9 | { 10 | // 11 | // This is a Data Oriented Design test that will show how 12 | // Struct of arrays has a much better Data Access characteristics and data locality. 13 | // 14 | // SOA [V0,V0,V0,V1,V1,V1] 15 | // AOS [V0,V1,V0,V1,V0,V1] 16 | // 17 | // For AOS for Structs or Classes we will have an 80 pad between which means that we will update each cache line 18 | // to increment the v0 value: 19 | // <0x281AF76B740> 80 80 20 | // <0x281AF76B790> 80 80 21 | // <0x281AF76B7E0> 80 80 22 | // 23 | // For SOA we will update each array value and reuse the same cache line: 24 | // Int32[] <0x216493DB6B8> 32 792 32 792 25 | // Int32[] <0x216493E36E8> 32 792 32 792 26 | // Int32[] <0x216493EB718> 32 792 32 792 27 | // 28 | // SOA is better in general for partial object updates while AOS is good when we want to contantly update entire 29 | // objects wich is almost never :) 30 | // 31 | public void TestAOS() 32 | { 33 | int steps = 1024 * 1024; 34 | int soaSize = 1024 * 8; 35 | 36 | SimpleStruct[] arrayOfStructs = new SimpleStruct[soaSize]; 37 | 38 | for (int i = 0; i < arrayOfStructs.Length; i++) 39 | arrayOfStructs[i] = new SimpleStruct(); 40 | 41 | for (int s = 0; s < steps; s++) 42 | { 43 | for (int i = 0; i < soaSize; i++) 44 | { 45 | arrayOfStructs[i].v0++; 46 | } 47 | } 48 | } 49 | 50 | public void TestSOA() 51 | { 52 | int steps = 1024 * 1024; 53 | int soaSize = 1024 * 8; 54 | 55 | SOAStruct soa = new SOAStruct(soaSize); 56 | 57 | for (int s = 0; s < steps; s++) 58 | { 59 | for (int i = 0; i < soaSize; i++) 60 | { 61 | soa.v0[i]++; 62 | } 63 | } 64 | } 65 | } 66 | 67 | class SOAStruct 68 | { 69 | public int[] v0; 70 | public int[] v1; 71 | public int[] v2; 72 | public int[] v3; 73 | public int[] v4; 74 | public int[] v5; 75 | public int[] v6; 76 | public int[] v7; 77 | public int[] v8; 78 | public int[] v9; 79 | public int[] v10; 80 | public int[] v11; 81 | public int[] v12; 82 | public int[] v13; 83 | public int[] v14; 84 | public int[] v15; 85 | 86 | public SOAStruct(int n) 87 | { 88 | v0 = new int[n]; 89 | v1 = new int[n]; 90 | v2 = new int[n]; 91 | v3 = new int[n]; 92 | v4 = new int[n]; 93 | v5 = new int[n]; 94 | v6 = new int[n]; 95 | v7 = new int[n]; 96 | v8 = new int[n]; 97 | v9 = new int[n]; 98 | v10 = new int[n]; 99 | v11 = new int[n]; 100 | v12 = new int[n]; 101 | v13 = new int[n]; 102 | v14 = new int[n]; 103 | v15 = new int[n]; 104 | } 105 | 106 | } 107 | 108 | class SimpleStruct 109 | { 110 | public int v0; 111 | public int v1; 112 | public int v2; 113 | public int v3; 114 | public int v4; 115 | public int v5; 116 | public int v6; 117 | public int v7; 118 | public int v8; 119 | public int v9; 120 | public int v10; 121 | public int v11; 122 | public int v12; 123 | public int v13; 124 | public int v14; 125 | public int v15; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /DoDSamples/Samples/ArrayEnumerableByRef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DoDSamples 6 | { 7 | public readonly struct ArrayEnumerableByRef 8 | { 9 | private readonly T[] _target; 10 | 11 | public ArrayEnumerableByRef(T[] target) => _target = target; 12 | 13 | public Enumerator GetEnumerator() => new Enumerator(_target); 14 | 15 | public struct Enumerator 16 | { 17 | private readonly T[] _target; 18 | 19 | private int _index; 20 | 21 | public Enumerator(T[] target) 22 | { 23 | _target = target; 24 | _index = -1; 25 | } 26 | 27 | public readonly ref T Current 28 | { 29 | get 30 | { 31 | if (_target is null || _index < 0 || _index > _target.Length) 32 | { 33 | throw new InvalidOperationException(); 34 | } 35 | return ref _target[_index]; 36 | } 37 | } 38 | 39 | public bool MoveNext() => ++_index < _target.Length; 40 | 41 | public void Reset() => _index = -1; 42 | } 43 | } 44 | 45 | public static class ArrayExtensions 46 | { 47 | public static ArrayEnumerableByRef ToEnumerableByRef(this T[] array) => new ArrayEnumerableByRef(array); 48 | public static ArrayEnumerableByRef ToEnumerableByRef(this List list) => new ArrayEnumerableByRef(list.ToArray()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DoDSamples/Samples/Benchmarks/InstructionAndDataDependency.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Order; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace DoDSamples 8 | { 9 | [Orderer(SummaryOrderPolicy.SlowestToFastest, MethodOrderPolicy.Declared)] 10 | public class InstructionAndDataDependency 11 | { 12 | 13 | //[Benchmark] 14 | //public void InsDependency() 15 | //{ 16 | // InstructionLevelDependency.TestDependency(); 17 | //} 18 | 19 | //[Benchmark] 20 | //public void InsIndependency() 21 | //{ 22 | // InstructionLevelDependency.TestIndependency(); 23 | //} 24 | 25 | [Benchmark] 26 | public void InsIndependencySumUnroll() 27 | { 28 | InstructionLevelDependency.TestIndependencyUnrolledSum(); 29 | } 30 | 31 | [Benchmark] 32 | public void InsDependencySumUnroll() 33 | { 34 | InstructionLevelDependency.TestDependencyUnrolledSum(); 35 | } 36 | 37 | [Benchmark] 38 | public void InsDependencySum() 39 | { 40 | InstructionLevelDependency.TestSum(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DoDSamples/Samples/Benchmarks/OOPvsDoDParsers.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Configs; 3 | using BenchmarkDotNet.Order; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Runtime.Intrinsics; 8 | using System.Runtime.Intrinsics.X86; 9 | using System.Text; 10 | 11 | namespace DoDSamples.Samples 12 | { 13 | 14 | [Orderer(SummaryOrderPolicy.SlowestToFastest, MethodOrderPolicy.Declared)] 15 | public class OOPvsDoDParsersToLower 16 | { 17 | [Params(100)] 18 | public int Samples = 1; 19 | 20 | private string[] lines = null; 21 | 22 | [GlobalSetup(Targets = new[] 23 | { 24 | nameof(ToLower), 25 | nameof(ToLowerASCIIInPlace_SIMD) 26 | })] 27 | 28 | public void GlobalSetup() 29 | { 30 | lines = File.ReadAllLines(@"C:\Users\Y700-17\Desktop\DataOrientedDesign\DoDSamples\DoDSamples\Samples\POParser\Sample.po"); 31 | } 32 | 33 | [Benchmark] 34 | public void ToLowerASCIIInPlace_SIMD() 35 | { 36 | DoDPOParser_v5 parser = new DoDPOParser_v5(); 37 | var dodLines_v5 = parser.Parse(lines, out var index); 38 | 39 | for (int s = 0; s < Samples; s++) 40 | { 41 | ToLowerASCIIInPlace_SIMD(dodLines_v5.AllNotes.ToString()); 42 | } 43 | } 44 | 45 | [Benchmark] 46 | public void ToLower() 47 | { 48 | DoDPOParser_v5 parser = new DoDPOParser_v5(); 49 | var dodLines_v5 = parser.Parse(lines, out var index); 50 | 51 | for (int s = 0; s < Samples; s++) 52 | { 53 | dodLines_v5.AllNotes.ToString().ToLower(); 54 | } 55 | } 56 | 57 | public static unsafe void ToLowerASCIIInPlace_SIMD(string text) 58 | { 59 | const int upperIntOffset = 2097184; 60 | const int upperCharOffset = 32; 61 | 62 | Vector128 vresult = Vector128.Zero; 63 | Vector128 add = Vector128.Create(upperIntOffset); 64 | 65 | var len = text.Length; 66 | 67 | fixed (char* pSource = text) 68 | { 69 | int i = 0; 70 | int lastBlockIndex = len - (len % 8); 71 | 72 | while (i < lastBlockIndex) 73 | { 74 | int* c = (int*)(pSource + i); 75 | 76 | vresult = Sse2.LoadVector128(c); 77 | vresult = Sse2.Or(vresult, add); 78 | 79 | Sse2.Store(c, vresult); 80 | 81 | i += 8; 82 | } 83 | 84 | while (i < len) 85 | { 86 | char* c = (char*)(pSource + i); 87 | *c = (Char)(*c | upperCharOffset); 88 | 89 | i += 1; 90 | } 91 | } 92 | } 93 | 94 | } 95 | 96 | 97 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] 98 | [CategoriesColumn] 99 | [Orderer(SummaryOrderPolicy.SlowestToFastest, MethodOrderPolicy.Declared)] 100 | public class OOPvsDoDParsers 101 | { 102 | [Params(100)] 103 | public int Samples = 1; 104 | 105 | private string[] lines = null; 106 | private string text = null; 107 | 108 | [GlobalSetup(Targets = new[] 109 | { 110 | //nameof(DoD_Parser_ListOfStructs), 111 | nameof(DoD_Parser_ArrayOfStructs), 112 | //nameof(DoD_Parser_ListOfStructs_Split), 113 | nameof(DoD_Parser_ArrayOfStructs_Split), 114 | nameof(DoD_Parser_ArrayOfStructs_HotColdSplit), 115 | nameof(DoD_Parser_StructOfArrays), 116 | nameof(DoD_Parser_StructOfArrays_SIMD), 117 | nameof(OOP_Parser_StateMachine), 118 | 119 | })] 120 | 121 | public void GlobalSetup() 122 | { 123 | lines = File.ReadAllLines(@"C:\Users\Y700-17\Desktop\DataOrientedDesign\DoDSamples\DoDSamples\Samples\POParser\Sample.po"); 124 | } 125 | 126 | [GlobalSetup(Targets = new[] { nameof(OOP_Parser_Regex) })] 127 | public void GlobalSetupA() 128 | { 129 | text = File.ReadAllText(@"C:\Users\Y700-17\Desktop\DataOrientedDesign\DoDSamples\DoDSamples\Samples\POParser\Sample.po"); 130 | } 131 | 132 | [BenchmarkCategory("OOP")] 133 | [Benchmark(Baseline = true)] 134 | public void OOP_Parser_Regex() 135 | { 136 | POParser parser = new POParser(); 137 | var lines_v0 = parser.Parse(text); 138 | 139 | for (int s = 0; s < Samples; s++) 140 | { 141 | double sum = 0; 142 | foreach (var line in lines_v0) 143 | { 144 | sum += line.Amount; 145 | } 146 | } 147 | } 148 | 149 | [BenchmarkCategory("OOP")] 150 | [Benchmark] 151 | public void OOP_Parser_StateMachine() 152 | { 153 | POParser_Fast parser = new POParser_Fast(); 154 | var lines_v0 = parser.Parse(lines); 155 | 156 | for (int s = 0; s < Samples; s++) 157 | { 158 | double sum = 0; 159 | foreach (var line in lines_v0) 160 | { 161 | sum += line.Amount; 162 | } 163 | } 164 | } 165 | 166 | //[BenchmarkCategory("DoD")] 167 | //[Benchmark] 168 | //public void DoD_Parser_ListOfStructs() 169 | //{ 170 | // DoDPOParser_v1 parser = new DoDPOParser_v1(); 171 | // var lines_v1 = parser.Parse(lines); 172 | 173 | // for (int s = 0; s < Samples; s++) 174 | // { 175 | // double sum = 0; 176 | 177 | // foreach (var line in lines_v1) 178 | // { 179 | // sum += line.Amount; 180 | // } 181 | // } 182 | //} 183 | 184 | [BenchmarkCategory("DoD")] 185 | [Benchmark(Baseline = true)] 186 | public void DoD_Parser_ArrayOfStructs() 187 | { 188 | DoDPOParser_v1a parser = new DoDPOParser_v1a(); 189 | var lines_v1 = parser.Parse(lines, out int index); 190 | 191 | for (int s = 0; s < Samples; s++) 192 | { 193 | double sum = 0; 194 | 195 | foreach (var line in lines_v1) 196 | { 197 | sum += line.Amount; 198 | } 199 | } 200 | } 201 | 202 | //[BenchmarkCategory("DoD")] 203 | //[Benchmark] 204 | //public void DoD_Parser_ListOfStructs_Split() 205 | //{ 206 | // DoDPOParser_v2 parser = new DoDPOParser_v2(); 207 | // var lines_v2 = parser.Parse(lines); 208 | 209 | // for (int s = 0; s < Samples; s++) 210 | // { 211 | // double sum = 0; 212 | 213 | // foreach (var line in lines_v2) 214 | // { 215 | // sum += line.Amount; 216 | // } 217 | // } 218 | //} 219 | 220 | [BenchmarkCategory("DoD")] 221 | [Benchmark] 222 | public void DoD_Parser_ArrayOfStructs_Split() 223 | { 224 | DoDPOParser_v2a parser = new DoDPOParser_v2a(); 225 | var lines_v2 = parser.Parse(lines, out int index); 226 | 227 | for (int s = 0; s < Samples; s++) 228 | { 229 | double sum = 0; 230 | 231 | foreach (var line in lines_v2) 232 | { 233 | sum += line.Amount; 234 | } 235 | } 236 | } 237 | 238 | [BenchmarkCategory("DoD")] 239 | [Benchmark] 240 | public void DoD_Parser_ArrayOfStructs_HotColdSplit() 241 | { 242 | DoDPOParser_v3 parser = new DoDPOParser_v3(); 243 | var lines_v3 = parser.Parse(lines); 244 | 245 | for (int s = 0; s < Samples; s++) 246 | { 247 | double sum = 0; 248 | 249 | for (int i = 0; i < lines_v3.Count; i++) 250 | { 251 | sum += lines_v3[i].Amount; 252 | } 253 | } 254 | } 255 | 256 | [BenchmarkCategory("DoD")] 257 | [Benchmark] 258 | public void DoD_Parser_StructOfArrays() 259 | { 260 | DoDPOParser_v4 parser = new DoDPOParser_v4(); 261 | var dodLines_v4 = parser.Parse(lines, out var index); 262 | 263 | for (int s = 0; s < Samples; s++) 264 | { 265 | double sum = 0; 266 | for (int i = 0; i < index; i++) 267 | { 268 | sum += dodLines_v4.Amount[i]; 269 | } 270 | } 271 | } 272 | 273 | [BenchmarkCategory("DoD")] 274 | [Benchmark] 275 | public void DoD_Parser_StructOfArrays_SIMD() 276 | { 277 | DoDPOParser_v4 parser = new DoDPOParser_v4(); 278 | var dodLines_v4 = parser.Parse(lines, out var index); 279 | 280 | for (int s = 0; s < Samples; s++) 281 | { 282 | double sum = SumAmount(dodLines_v4, index); 283 | } 284 | } 285 | 286 | 287 | public unsafe double SumAmount(DoDPOLines_v4 source, int len) 288 | { 289 | double result; 290 | 291 | fixed (double* pSource = source.Amount) 292 | { 293 | Vector128 vresult = Vector128.Zero; 294 | 295 | int i = 0; 296 | int lastBlockIndex = len - (len % 2); 297 | 298 | while (i < lastBlockIndex) 299 | { 300 | vresult = Sse2.Add(vresult, Sse2.LoadVector128(pSource + i)); 301 | i += 2; 302 | } 303 | 304 | vresult = Ssse3.HorizontalAdd(vresult, vresult); 305 | result = vresult.ToScalar(); 306 | 307 | while (i < len) 308 | { 309 | result += pSource[i]; 310 | i += 1; 311 | } 312 | } 313 | 314 | return result; 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /DoDSamples/Samples/Benchmarks/RefListvsList.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace DoDSamples.Samples.Benchmarks 7 | { 8 | 9 | //public class RefListVSList 10 | //{ 11 | // private List list; 12 | // private RefList refList; 13 | // private const int size = 1_000_000; 14 | 15 | // [IterationSetup(Target = nameof(List))] 16 | // public void SetUpList() 17 | // { 18 | // list = new List(size); 19 | // for (int i = 0; i < size; i++) 20 | // { 21 | // list.Add(new POLine_v1() { ID = i }); 22 | // } 23 | // } 24 | 25 | // [IterationSetup(Target = nameof(RefList))] 26 | // public void SetUpRefList() 27 | // { 28 | // refList = new RefList(size); 29 | // for (int i = 0; i < size; i++) 30 | // { 31 | // refList.Add(new POLine_v1() { ID = i }); 32 | // } 33 | // } 34 | 35 | // [Benchmark] 36 | // public void List() 37 | // { 38 | // double sum = 0; 39 | 40 | // foreach(var item in list) 41 | // { 42 | // sum += item.Amount; 43 | // } 44 | // } 45 | 46 | // [Benchmark] 47 | // public void RefList() 48 | // { 49 | // double sum = 0; 50 | 51 | // foreach (var item in refList) 52 | // { 53 | // sum += item.Amount; 54 | // } 55 | // } 56 | //} 57 | } 58 | -------------------------------------------------------------------------------- /DoDSamples/Samples/Benchmarks/RowsVsCols.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace DoDSamples 7 | { 8 | 9 | // 10 | // This example demonstrates the performance 11 | // of Row vs Col access using a flat array. 12 | // It also shows how much performance can improve if we simply 13 | // use our cache line to the full extent. 14 | // 15 | // It shows how proper datta access patterns are important, 16 | // even in a single threaded enviroment. 17 | // 18 | public class RowsVsCols 19 | { 20 | private const int rows = 10_000; 21 | private const int cols = 10_000; 22 | private int[] array = new int[rows * cols]; 23 | 24 | [IterationSetup] 25 | public void SetUp() 26 | { 27 | array = new int[rows * cols]; 28 | } 29 | 30 | [Benchmark] 31 | public void Rows() 32 | { 33 | for(int i = 0; i < cols; i++) 34 | { 35 | for(int j = 0; j < rows; j++) 36 | { 37 | array[(i * cols) + j]++; 38 | } 39 | } 40 | } 41 | 42 | [Benchmark] 43 | public void Cols() 44 | { 45 | for (int i = 0; i < rows; i++) 46 | { 47 | for (int j = 0; j < cols; j++) 48 | { 49 | array[(j * cols) + i]++; 50 | } 51 | } 52 | } 53 | 54 | [Benchmark] 55 | public void ColsStride() 56 | { 57 | for (int i = 0; i < rows; i += 8) 58 | { 59 | for (int j = 0; j < cols; j++) 60 | { 61 | array[(j * cols) + i + 0]++; 62 | array[(j * cols) + i + 1]++; 63 | array[(j * cols) + i + 2]++; 64 | array[(j * cols) + i + 3]++; 65 | array[(j * cols) + i + 4]++; 66 | array[(j * cols) + i + 5]++; 67 | array[(j * cols) + i + 6]++; 68 | array[(j * cols) + i + 7]++; 69 | //Console.WriteLine((j * cols) + i); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DoDSamples/Samples/BitOperations.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Diagnostics.Runtime.Utilities; 2 | using Microsoft.Diagnostics.Tracing.Parsers.Kernel; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.Intrinsics; 6 | using System.Runtime.Intrinsics.X86; 7 | using System.Text; 8 | 9 | namespace DoDSamples.Samples 10 | { 11 | public class BitOperations 12 | { 13 | 14 | // 15 | // Branch Elimination Example: lets count even numbers 16 | // 17 | 18 | 19 | 20 | public static int CountEven(int[] numbers) 21 | { 22 | int counter = 0; 23 | for(int i = 0; i < numbers.Length; i++) 24 | { 25 | if(numbers[i] % 2 == 0) 26 | { 27 | counter++; 28 | } 29 | } 30 | 31 | return counter; 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | public static int CountEvenBranchFree(int[] numbers) 41 | { 42 | int counter = 0; 43 | for (int i = 0; i < numbers.Length; i++) 44 | { 45 | var add = numbers[i] & 1; 46 | counter += (1 - add); 47 | } 48 | 49 | return counter; 50 | } 51 | 52 | 53 | 54 | 55 | public static int CountEvenBranchFreeBetter(int[] numbers) 56 | { 57 | int counter = 0; 58 | for (int i = 0; i < numbers.Length; i++) 59 | { 60 | var odd = numbers[i] & 1; 61 | counter += odd; 62 | } 63 | 64 | // Take all odd numbers and substract them from the set 65 | // leaving only even numbers 66 | return numbers.Length - counter; 67 | } 68 | 69 | 70 | 71 | 72 | 73 | public static unsafe int CountEvenSIMD(int[] numbers) 74 | { 75 | int counter = 0; 76 | int len = numbers.Length; 77 | 78 | fixed (int* num = numbers) 79 | { 80 | Vector128 vresult = Vector128.Zero; 81 | Vector128 ones = Vector128.Create(1); 82 | 83 | int i = 0; 84 | int lastBlockIndex = len - (len % 4); 85 | 86 | while (i < lastBlockIndex) 87 | { 88 | var vec = Sse2.LoadVector128(num + i); 89 | var odds = Sse2.And(vec, ones); 90 | vresult = Sse2.Add(vresult, odds); 91 | 92 | i += 4; 93 | } 94 | 95 | vresult = Ssse3.HorizontalAdd(vresult, vresult); 96 | vresult = Ssse3.HorizontalAdd(vresult, vresult); 97 | 98 | counter = vresult.ToScalar(); 99 | 100 | while (i < len) 101 | { 102 | var odd = numbers[i] & 1; 103 | counter += odd; 104 | 105 | i += 1; 106 | } 107 | } 108 | 109 | return numbers.Length - counter; 110 | } 111 | 112 | public static int CountChar(string text, char needle) 113 | { 114 | int count = 0; 115 | 116 | foreach(var c in text) 117 | { 118 | if (c == needle) 119 | count++; 120 | } 121 | 122 | return count; 123 | } 124 | 125 | 126 | 127 | 128 | 129 | 130 | private static int[] jump_table = new int[256]; 131 | 132 | public static void InitJumpTable(char needle) 133 | { 134 | int cIdx = (byte)needle; 135 | 136 | for(int i = 0; i < jump_table.Length; i++) 137 | { 138 | jump_table[i] = 0; 139 | if (i == cIdx) 140 | { 141 | jump_table[i] = 1; 142 | } 143 | } 144 | } 145 | 146 | public static int CountCharJumpTable(string text, char needle) 147 | { 148 | int count = 0; 149 | 150 | foreach (var c in text) 151 | { 152 | count += jump_table[(byte)c]; 153 | } 154 | 155 | return count; 156 | } 157 | 158 | public static int CountCharWORD(string text, char needle) 159 | { 160 | int count = 0; 161 | byte charToFind = (byte)needle; 162 | byte before = (byte)(charToFind - 1); 163 | byte after = (byte)(charToFind + 1); 164 | 165 | unsafe 166 | { 167 | fixed (char* ch = text) 168 | { 169 | int len = text.Length; 170 | 171 | int i = 0; 172 | int lastBlockIndex = len - (len % 4); 173 | 174 | while (i < lastBlockIndex) 175 | { 176 | ulong* p = (ulong*)(ch + i); 177 | var result = (int)CountBetween(*p, before, after); 178 | count += result; 179 | 180 | i += 4; 181 | } 182 | 183 | while (i < len) 184 | { 185 | char* c = (char*)(ch + i); 186 | if (*c == charToFind) 187 | { 188 | count++; 189 | } 190 | i += 1; 191 | } 192 | 193 | return count - len; 194 | } 195 | } 196 | } 197 | 198 | public static int CountWhitespaceWORDLess(string text) 199 | { 200 | int count = 0; 201 | byte charToFind = (byte)' '; 202 | byte after = (byte)(charToFind + 1); 203 | 204 | unsafe 205 | { 206 | fixed (char* ch = text) 207 | { 208 | int len = text.Length; 209 | 210 | int i = 0; 211 | int lastBlockIndex = len - (len % 4); 212 | 213 | while (i < lastBlockIndex) 214 | { 215 | ulong* p = (ulong*)(ch + i); 216 | var result = (int)CountLess(*p, after); 217 | count += result; 218 | 219 | i += 4; 220 | } 221 | 222 | while (i < len) 223 | { 224 | char* c = (char*)(ch + i); 225 | if (*c == charToFind) 226 | { 227 | count++; 228 | } 229 | i += 1; 230 | } 231 | 232 | return count - len; 233 | } 234 | } 235 | } 236 | 237 | public static int CountCharWORDCompact(string text, char needle) 238 | { 239 | int count = 0; 240 | byte charToFind = (byte)needle; 241 | byte before = (byte)(charToFind - 1); 242 | byte after = (byte)(charToFind + 1); 243 | 244 | unsafe 245 | { 246 | fixed (char* ch = text) 247 | { 248 | int len = text.Length; 249 | 250 | int i = 0; 251 | int lastBlockIndex = len - (len % 4); 252 | 253 | while (i < lastBlockIndex) 254 | { 255 | ulong* p = (ulong*)(ch + i); 256 | 257 | var has = HasValue(*p, charToFind); 258 | if (has > 0) 259 | { 260 | var result = CountBetween(*p, before, after); 261 | count += (int)result; 262 | } 263 | 264 | i += 4; 265 | } 266 | 267 | while (i < len) 268 | { 269 | char* c = (char*)(ch + i); 270 | if (*c == charToFind) 271 | { 272 | count++; 273 | } 274 | i += 1; 275 | } 276 | 277 | return count; 278 | } 279 | } 280 | } 281 | 282 | public static int CountWhitespaceWORDCompactLess(string text) 283 | { 284 | int count = 0; 285 | byte charToFind = (byte)' '; 286 | byte after = (byte)(charToFind + 1); 287 | 288 | unsafe 289 | { 290 | fixed (char* ch = text) 291 | { 292 | int len = text.Length; 293 | 294 | int i = 0; 295 | int lastBlockIndex = len - (len % 4); 296 | 297 | while (i < lastBlockIndex) 298 | { 299 | ulong* p = (ulong*)(ch + i); 300 | 301 | var has = HasValue(*p, charToFind); 302 | if (has > 0) 303 | { 304 | var result = CountLess(*p, after); 305 | count += (int)result; 306 | } 307 | 308 | i += 4; 309 | } 310 | 311 | while (i < len) 312 | { 313 | char* c = (char*)(ch + i); 314 | if (*c == charToFind) 315 | { 316 | count++; 317 | } 318 | i += 1; 319 | } 320 | 321 | return count; 322 | } 323 | } 324 | } 325 | 326 | // 327 | // Bit Twindling Hacks: 328 | // https://graphics.stanford.edu/~seander/bithacks.html 329 | // 330 | public static ulong HasZero(ulong v) 331 | { 332 | return (((v) - 0x0101010101010101UL) & ~(v) & 0x8080808080808080UL); 333 | } 334 | 335 | public static ulong HasValue(ulong x, byte n) 336 | { 337 | return (HasZero((x) ^ (~0UL / 255 * (n)))); 338 | } 339 | 340 | public static ulong HasLess(ulong x, byte n) 341 | { 342 | return (((x) - ~0UL / 255 * (n)) & ~(x) & ~0UL / 255 * 128); 343 | } 344 | 345 | public static ulong CountLess(ulong x, byte n) 346 | { 347 | return (((~0UL / 255 * (127 + ((ulong)n)) - ((x) & ~0UL / 255 * 127)) & ~(x) & ~0UL / 255 * 128) / 128 % 255); 348 | } 349 | 350 | public static ulong HasBetween(ulong x, byte m, byte n) 351 | { 352 | return ((~0UL / 255 * (127 + ((ulong)n)) 353 | - ((x) & ~0UL / 255 * 127) & 354 | ~(x) & ((x) & ~0UL / 255 * 127) + 355 | ~0UL / 255 * (127 - ((ulong)m))) & ~0UL / 255 * 128); 356 | } 357 | 358 | public static ulong CountBetween(ulong x, byte m, byte n) 359 | { 360 | return HasBetween(x, m, n) / 128 % 255; 361 | } 362 | 363 | 364 | 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /DoDSamples/Samples/BlockList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Text; 5 | 6 | namespace DoDSamples 7 | { 8 | public class BlockList 9 | { 10 | private ListBlock[] blocks = new ListBlock[32]; 11 | private int blockIndex = 0; 12 | private int blockSize = 512; 13 | private ListBlock mainBlock = null; 14 | 15 | public BlockList() 16 | { 17 | mainBlock = new ListBlock(blockSize); 18 | blocks[blockIndex++] = mainBlock; 19 | } 20 | 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | public void Add(T value) 23 | { 24 | var blockFull = mainBlock.Add(value); 25 | // 26 | // Add another block 27 | // 28 | if (blockFull) 29 | { 30 | mainBlock = new ListBlock(blockSize); 31 | blocks[blockIndex++] = mainBlock; 32 | } 33 | } 34 | 35 | public T Get(int index) 36 | { 37 | var blockId = index / blockSize; 38 | return blocks[blockId].Get(index - (blockId * blockSize)); 39 | } 40 | 41 | public ListBlock GetBlock(int index, out int indexInBlock) 42 | { 43 | var blockId = index / blockSize; 44 | indexInBlock = index - (blockId * blockSize); 45 | return blocks[blockId]; 46 | } 47 | 48 | public void Set(int index, T value) 49 | { 50 | var blockId = index / blockSize; 51 | blocks[blockId].Set(index - (blockId * blockSize), value); 52 | } 53 | 54 | public T this[int index] 55 | { 56 | get { return Get(index); } 57 | set { Set(index, value); } 58 | } 59 | 60 | public class ListBlock 61 | { 62 | public ListBlock(int blockSize) 63 | { 64 | array = new T[blockSize]; 65 | } 66 | 67 | public T Get(int index) 68 | { 69 | return array[index]; 70 | } 71 | 72 | public void Set(int index, T value) 73 | { 74 | array[index] = value; 75 | } 76 | 77 | private T[] array; 78 | private int index = 0; 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public bool Add(T value) 82 | { 83 | array[index++] = value; 84 | return (index >= array.Length); 85 | } 86 | } 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /DoDSamples/Samples/CacheSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DoDSamples 6 | { 7 | public class CacheSize 8 | { 9 | // 10 | // This class will test the cache line sizes for various levels 11 | // The idea is simple: Move through the array loading a new cache line on every step 12 | // if we exceed the size of the array we start from the begining and continue. 13 | // We do this for a set amount of steps and mesure the assigment time, if we move to the 14 | // next cache level there will be a significant slowdown. 15 | // 16 | // How to Perform the test: 17 | // 18 | // int steps = 65536 * 1024; 19 | // int[] array = new int[1024 * 1024 * 1]; 20 | // 21 | // var e = Utils.Measure(() => c.TestK(steps, array)); 22 | // e = e* 1000; //Scale the time 23 | // Console.WriteLine((double) e / (double) steps); 24 | // 25 | public static void TestK(int steps, int[] array) 26 | { 27 | int lengthMod = array.Length - 1; 28 | 29 | for (int i = 0; i < steps; i++) 30 | { 31 | array[(i * 16) & lengthMod]++; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DoDSamples/Samples/InstructionLevelDependency.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.Intrinsics; 5 | using System.Runtime.Intrinsics.X86; 6 | using System.Text; 7 | 8 | namespace DoDSamples 9 | { 10 | #region desc 11 | // 12 | // Instruction Level Dependency: 13 | // Good compilers will do it's best to break dependency chains 14 | // but sometimes it's just impossible and it's up to the user to break non trival chains. 15 | // 16 | #endregion 17 | 18 | public class InstructionLevelDependency 19 | { 20 | public static int steps = 128 * 1024 * 1024; 21 | public static readonly int i32Bits = sizeof(int) * 8 - 1; 22 | 23 | public static int TestDependency() 24 | { 25 | int a = 0; 26 | 27 | for (int i = 0; i < steps; i++) 28 | { 29 | a++; 30 | a++; 31 | a++; 32 | a++; 33 | } 34 | 35 | return a; 36 | } 37 | 38 | public static int TestIndependency() 39 | { 40 | int a = 0, b = 0, c = 0, d = 0; 41 | 42 | for (int i = 0; i < steps; i++) 43 | { 44 | a++; 45 | b++; 46 | c++; 47 | d++; 48 | } 49 | 50 | return a + b + c + d; 51 | } 52 | 53 | public static void TestSum() 54 | { 55 | double[] x = new double[steps]; 56 | double sum = 0; 57 | 58 | for (int i = 0; i < steps; i++) 59 | { 60 | sum += x[i]; 61 | } 62 | } 63 | 64 | public static void TestDependencyUnrolledSum() 65 | { 66 | double[] x = new double[steps]; 67 | double sum = 0; 68 | 69 | int i = 0; 70 | for (; i < steps; i += 4) 71 | { 72 | sum += x[i]; 73 | sum += x[i + 1]; 74 | sum += x[i + 2]; 75 | sum += x[i + 3]; 76 | } 77 | // Sum residuals 78 | for (; i < steps; i++) 79 | { 80 | sum += x[i]; 81 | } 82 | } 83 | 84 | public static void TestIndependencyUnrolledSum() 85 | { 86 | int steps = 64 * 1024 * 1024; 87 | 88 | double[] x = new double[steps]; 89 | double sum = 0; 90 | double sa = 0, sb = 0, sc = 0, sd = 0; 91 | 92 | int i = 0; 93 | for (; i < steps; i += 4) 94 | { 95 | sa += x[i]; 96 | sb += x[i + 1]; 97 | sc += x[i + 2]; 98 | sd += x[i + 3]; 99 | } 100 | 101 | // Sum residuals 102 | for (; i < steps; i++) 103 | { 104 | sum += x[i]; 105 | } 106 | 107 | sum = sa + sb + sc + sd; 108 | } 109 | 110 | public static void TestIndependencyUnrolledSum2() 111 | { 112 | int steps = 64 * 1024 * 1024; 113 | 114 | double[] x = new double[steps]; 115 | double sum = 0; 116 | double sa = 0, sb = 0, sc = 0, sd = 0; 117 | 118 | int i = 0; 119 | for (; i < steps; i += 4) 120 | { 121 | sa += x[i]; 122 | sb += x[i + 1]; 123 | sc += x[i + 2]; 124 | sd += x[i + 3]; 125 | } 126 | 127 | // Sum residuals 128 | for (; i < steps; i++) 129 | { 130 | sum += x[i]; 131 | } 132 | 133 | sum = (sa + sb) + (sc + sd); 134 | } 135 | 136 | public static void TestTrueDependence(int[] x) 137 | { 138 | var min = 0; 139 | 140 | for (int i = 0; i < x.Length; i++) 141 | { 142 | min = Math.Min(min, x[i]); 143 | //min = MinBranch(min, x[i]); 144 | } 145 | } 146 | 147 | public static void TestTrueIndependence(int[] x) 148 | { 149 | var min = 0; 150 | var m1 = 0; 151 | var m2 = 0; 152 | var m3 = 0; 153 | var m4 = 0; 154 | 155 | for (int i = 0; i < x.Length; i += 4) 156 | { 157 | m1 = MinBranchFree(m1, x[i]); 158 | m2 = MinBranchFree(m2, x[i + 1]); 159 | m3 = MinBranchFree(m3, x[i + 2]); 160 | m4 = MinBranchFree(m4, x[i + 3]); 161 | } 162 | 163 | min = MinBranchFree(m1, MinBranchFree(m2, MinBranchFree(m3, m4))); 164 | } 165 | 166 | public static void TestTrueIndependenceAVX(int[] x) 167 | { 168 | var min = 0; 169 | var m1 = 0; 170 | var m2 = 0; 171 | var m3 = 0; 172 | var m4 = 0; 173 | 174 | for (int i = 0; i < x.Length; i += 4) 175 | { 176 | m1 = AVXMin(m1, x[i]); 177 | m2 = AVXMin(m2, x[i + 1]); 178 | m3 = AVXMin(m3, x[i + 2]); 179 | m4 = AVXMin(m4, x[i + 3]); 180 | } 181 | 182 | min = AVXMin(m1, AVXMin(m2, AVXMin(m3, m4))); 183 | } 184 | 185 | public static int TestTrueIndependenceAVXVec(int[] x) 186 | { 187 | return AVXVecMin(x); 188 | } 189 | 190 | public static int TestTrueIndependenceAVXVecIndependant(int[] x) 191 | { 192 | return AVXVecMinIndependent(x); 193 | } 194 | 195 | public static void TestTrueDependenceBranchFree(int[] x) 196 | { 197 | int min = int.MaxValue, inc = 0; 198 | for (int i = 0; i < x.Length; i++) 199 | { 200 | inc = IsMinBranchFree(min, x[i]); 201 | var delta = min - x[i]; 202 | min = min - (delta * inc); 203 | } 204 | } 205 | 206 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 207 | public static int IsMinBranchFree(int x, int y) 208 | { 209 | var v = x - y; 210 | return (int)((1 ^ ((uint)v >> (31)))); 211 | } 212 | 213 | 214 | public static void TestTrueIndependenceDelta(int[] x) 215 | { 216 | var max = int.MaxValue; 217 | int[] m = new int[4] { max, max, max, max }; 218 | var inc = 0; 219 | for (int i = 0; i < x.Length; i += 4) 220 | { 221 | for (int w = 0; w < m.Length; w++) 222 | { 223 | inc = IsMinBranchFree(m[w], x[i + w]); 224 | var delta = m[w] - x[i + w]; 225 | m[w] = m[w] - (delta * inc); 226 | } 227 | } 228 | // todo: residuals + min(m[0],m[1],m[2],m[3]) 229 | } 230 | 231 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 232 | public static int MinBranchFree(int x, int y) 233 | { 234 | return y + ((x - y) & ((x - y) >> (31))); 235 | } 236 | 237 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 238 | public static int AVXMin(int x, int y) 239 | { 240 | var v1 = Vector128.CreateScalarUnsafe(x); 241 | var v2 = Vector128.CreateScalarUnsafe(y); 242 | return Avx.Min(v1, v2).ToScalar(); 243 | } 244 | 245 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 246 | public static unsafe int AVXVecMin(int[] x) 247 | { 248 | int len = x.Length; 249 | var min = Vector128.Create(int.MaxValue); 250 | fixed (int* pSource = x) 251 | { 252 | int i = 0; 253 | int lastBlockIndex = len - (len % 4); 254 | 255 | while (i < lastBlockIndex) { 256 | min = Avx.Min(min, Avx.LoadVector128(pSource + i)); 257 | i += 4; 258 | } 259 | var minValue = min.ToScalar(); 260 | while (i < len) { 261 | minValue = MinBranchFree(minValue, pSource[i]); 262 | i += 1; 263 | } 264 | return minValue; 265 | } 266 | } 267 | 268 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 269 | public static unsafe int AVXVecMinIndependent(int[] x) 270 | { 271 | int len = x.Length; 272 | var min1 = Vector128.Create(int.MaxValue); 273 | var min2 = Vector128.Create(int.MaxValue); 274 | 275 | fixed (int* pSource = x) 276 | { 277 | int i = 0; 278 | int lastBlockIndex = len - (len % 8); 279 | 280 | while (i < lastBlockIndex) 281 | { 282 | min1 = Avx.Min(min1, Avx.LoadVector128(pSource + i)); 283 | min2 = Avx.Min(min2, Avx.LoadVector128(pSource + i + 4)); 284 | 285 | i += 8; 286 | } 287 | var minValue = min1.ToScalar() + min2.ToScalar(); 288 | while (i < len) 289 | { 290 | minValue = MinBranchFree(minValue, pSource[i]); 291 | i += 1; 292 | } 293 | return minValue; 294 | } 295 | } 296 | 297 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 298 | public static int MinBranch(int x, int y) 299 | { 300 | return (x <= y) ? x : y; 301 | } 302 | } 303 | } 304 | 305 | -------------------------------------------------------------------------------- /DoDSamples/Samples/MemoryModelDeadlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace DoDSamples 7 | { 8 | public class MemoryModelDeadlock 9 | { 10 | public void Test() 11 | { 12 | bool isSignaled = false; 13 | 14 | Thread master = new Thread(() => 15 | { 16 | Console.WriteLine($"{DateTime.UtcNow} [Thread:{Thread.CurrentThread.ManagedThreadId}] Waiting 1000ms and setting the Signal Variable"); 17 | Thread.Sleep(1000); 18 | 19 | Console.WriteLine($"{DateTime.UtcNow} [Thread:{Thread.CurrentThread.ManagedThreadId}] Signal!"); 20 | isSignaled = true; 21 | }); 22 | 23 | master.Start(); 24 | 25 | Thread slave = new Thread(() => 26 | { 27 | while (isSignaled == false) { } 28 | Console.WriteLine($"{DateTime.UtcNow} [Thread:{Thread.CurrentThread.ManagedThreadId}] I Was Signaled"); 29 | 30 | }); 31 | 32 | slave.Start(); 33 | 34 | //slave.Join(); 35 | //master.Join(); 36 | } 37 | 38 | SpinLock spinLock = new SpinLock(); 39 | 40 | public void Spin() 41 | { 42 | bool taken = false; 43 | for (int i = 0; i < 1000; i++) 44 | { 45 | taken = false; 46 | 47 | spinLock.Enter(ref taken); 48 | { 49 | int d = 0; 50 | for (int k = 0; k < 1000000; k++) 51 | d++; 52 | } 53 | spinLock.Exit(true); 54 | 55 | if (taken == false) 56 | Console.WriteLine(taken); 57 | } 58 | } 59 | 60 | public void SpinLockTest() 61 | { 62 | Thread[] threads = new Thread[32]; 63 | for(int i = 0; i < threads.Length; i++) 64 | { 65 | threads[i] = new Thread(() => Spin()); 66 | } 67 | 68 | for (int i = 0; i < threads.Length; i++) 69 | { 70 | threads[i].Start(); 71 | } 72 | 73 | for (int i = 0; i < threads.Length; i++) 74 | { 75 | threads[i].Join(); 76 | } 77 | 78 | Console.WriteLine("[Done]"); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DoDSamples.Samples 7 | { 8 | public struct POLine_v1 9 | { 10 | public int ID; 11 | public double Amount; 12 | public string Date; 13 | public string Time; 14 | public string Note; 15 | } 16 | 17 | public class DoDPOParser_v1 18 | { 19 | 20 | private enum State 21 | { 22 | ID, 23 | DateTime, 24 | Amount, 25 | Note 26 | } 27 | 28 | public List Parse(string path) => Parse(File.ReadAllLines(path)); 29 | 30 | public List Parse(string[] lines) 31 | { 32 | List transactionLines = new List(1000); 33 | POLine_v1 poLine = new POLine_v1(); 34 | 35 | State state = State.ID; 36 | foreach (var line in lines) 37 | { 38 | if (string.IsNullOrEmpty(line)) 39 | { 40 | if (state >= State.Note) 41 | { 42 | transactionLines.Add(poLine); 43 | state = State.ID; 44 | } 45 | 46 | continue; 47 | } 48 | switch (state) 49 | { 50 | case State.ID: 51 | { 52 | poLine.ID = int.Parse(line); 53 | 54 | state++; 55 | break; 56 | } 57 | case State.DateTime: 58 | { 59 | var dt = line.Split('T'); 60 | poLine.Date = dt[0]; 61 | poLine.Time = dt[1]; 62 | 63 | state++; 64 | break; 65 | } 66 | case State.Amount: 67 | { 68 | poLine.Amount = double.Parse(line); 69 | 70 | state++; 71 | break; 72 | } 73 | case State.Note: 74 | { 75 | poLine.Note += line; 76 | 77 | state++; 78 | break; 79 | } 80 | } 81 | } 82 | 83 | return transactionLines; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v1a.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DoDSamples.Samples 7 | { 8 | public class DoDPOParser_v1a 9 | { 10 | private enum State 11 | { 12 | ID, 13 | DateTime, 14 | Amount, 15 | Note 16 | } 17 | 18 | public POLine_v1[] Parse(string path, out int index) => Parse(File.ReadAllLines(path), out index); 19 | 20 | public POLine_v1[] Parse(string[] lines, out int index) 21 | { 22 | index = 0; 23 | 24 | POLine_v1[] transactionLines = new POLine_v1[1000]; 25 | POLine_v1 poLine = new POLine_v1(); 26 | 27 | State state = State.ID; 28 | foreach (var line in lines) 29 | { 30 | if (string.IsNullOrEmpty(line)) 31 | { 32 | if (state >= State.Note) 33 | { 34 | transactionLines[index] = poLine; 35 | state = State.ID; 36 | index++; 37 | } 38 | 39 | continue; 40 | } 41 | switch (state) 42 | { 43 | case State.ID: 44 | { 45 | poLine.ID = int.Parse(line); 46 | 47 | state++; 48 | break; 49 | } 50 | case State.DateTime: 51 | { 52 | var dt = line.Split('T'); 53 | poLine.Date = dt[0]; 54 | poLine.Time = dt[1]; 55 | 56 | state++; 57 | break; 58 | } 59 | case State.Amount: 60 | { 61 | poLine.Amount = double.Parse(line); 62 | 63 | state++; 64 | break; 65 | } 66 | case State.Note: 67 | { 68 | poLine.Note += line; 69 | 70 | state++; 71 | break; 72 | } 73 | } 74 | } 75 | 76 | return transactionLines; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Http.Headers; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace DoDSamples.Samples 9 | { 10 | 11 | // 12 | // You might thing that this is better setting the pack but if you think about it, this data set breaks 13 | // aligment, and thus all potential gains will be nullified by missaligment. 14 | // 15 | //[StructLayout(LayoutKind.Sequential, Pack = 4)] 16 | public struct POLine_v2 17 | { 18 | public int ID; 19 | public double Amount; 20 | public PODateTime PODateTime; 21 | public string Note; 22 | } 23 | 24 | public struct PODateTime 25 | { 26 | public string Date; 27 | public string Time; 28 | } 29 | 30 | 31 | public class DoDPOParser_v2 32 | { 33 | private enum State 34 | { 35 | ID, 36 | DateTime, 37 | Amount, 38 | Note 39 | } 40 | 41 | public List Parse(string path) => Parse(File.ReadAllLines(path)); 42 | 43 | public List Parse(string[] lines) 44 | { 45 | List transactionLines = new List(1000); 46 | 47 | POLine_v2 poLine = new POLine_v2(); 48 | 49 | State state = State.ID; 50 | foreach (var line in lines) 51 | { 52 | if (string.IsNullOrEmpty(line)) 53 | { 54 | if (state >= State.Note) 55 | { 56 | transactionLines.Add(poLine); 57 | state = State.ID; 58 | } 59 | 60 | continue; 61 | } 62 | switch (state) 63 | { 64 | case State.ID: 65 | { 66 | poLine.ID = int.Parse(line); 67 | 68 | state++; 69 | break; 70 | } 71 | case State.DateTime: 72 | { 73 | var dt = line.Split('T'); 74 | poLine.PODateTime.Date = dt[0]; 75 | poLine.PODateTime.Time = dt[1]; 76 | 77 | state++; 78 | break; 79 | } 80 | case State.Amount: 81 | { 82 | poLine.Amount = double.Parse(line); 83 | 84 | state++; 85 | break; 86 | } 87 | case State.Note: 88 | { 89 | poLine.Note += line; 90 | 91 | state++; 92 | break; 93 | } 94 | } 95 | } 96 | 97 | return transactionLines; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v2a.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Http.Headers; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace DoDSamples.Samples 9 | { 10 | 11 | public class DoDPOParser_v2a 12 | { 13 | private enum State 14 | { 15 | ID, 16 | DateTime, 17 | Amount, 18 | Note 19 | } 20 | 21 | public POLine_v2[] Parse(string path, out int index) => Parse(File.ReadAllLines(path), out index); 22 | 23 | public POLine_v2[] Parse(string[] lines, out int index) 24 | { 25 | index = 0; 26 | 27 | POLine_v2[] transactionLines = new POLine_v2[1000]; 28 | POLine_v2 poLine = new POLine_v2(); 29 | 30 | State state = State.ID; 31 | foreach (var line in lines) 32 | { 33 | if (string.IsNullOrEmpty(line)) 34 | { 35 | if (state >= State.Note) 36 | { 37 | transactionLines[index] = poLine; 38 | state = State.ID; 39 | index++; 40 | } 41 | 42 | continue; 43 | } 44 | switch (state) 45 | { 46 | case State.ID: 47 | { 48 | poLine.ID = int.Parse(line); 49 | 50 | state++; 51 | break; 52 | } 53 | case State.DateTime: 54 | { 55 | var dt = line.Split('T'); 56 | poLine.PODateTime.Date = dt[0]; 57 | poLine.PODateTime.Time = dt[1]; 58 | 59 | state++; 60 | break; 61 | } 62 | case State.Amount: 63 | { 64 | poLine.Amount = double.Parse(line); 65 | 66 | state++; 67 | break; 68 | } 69 | case State.Note: 70 | { 71 | poLine.Note += line; 72 | 73 | state++; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | return transactionLines; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace DoDSamples.Samples 9 | { 10 | 11 | // 12 | // You might thing that this is better setting the pack but if you think about it, this data set breaks 13 | // aligment, and thus all potential gains will be nullified by missaligment. 14 | // 15 | //[StructLayout(LayoutKind.Sequential, Pack = 4)] 16 | public struct POLine_v3 17 | { 18 | public double Amount; 19 | public POData POData; 20 | } 21 | 22 | public struct POData 23 | { 24 | public int ID; 25 | public string Date; 26 | public string Time; 27 | public string Note; 28 | } 29 | 30 | public class DoDPOParser_v3 31 | { 32 | private enum State 33 | { 34 | ID, 35 | DateTime, 36 | Amount, 37 | Note 38 | } 39 | 40 | public List Parse(string path) => Parse(File.ReadAllLines(path)); 41 | 42 | public List Parse(string[] lines) 43 | { 44 | List transactionLines = new List(1000); 45 | POLine_v3 poLine = new POLine_v3(); 46 | 47 | State state = State.ID; 48 | foreach (var line in lines) 49 | { 50 | if (string.IsNullOrEmpty(line)) 51 | { 52 | if (state >= State.Note) 53 | { 54 | transactionLines.Add(poLine); 55 | state = State.ID; 56 | } 57 | 58 | continue; 59 | } 60 | switch (state) 61 | { 62 | case State.ID: 63 | { 64 | poLine.POData.ID = int.Parse(line); 65 | 66 | state++; 67 | break; 68 | } 69 | case State.DateTime: 70 | { 71 | var dt = line.Split('T'); 72 | poLine.POData.Date = dt[0]; 73 | poLine.POData.Time = dt[1]; 74 | 75 | state++; 76 | break; 77 | } 78 | case State.Amount: 79 | { 80 | poLine.Amount = double.Parse(line); 81 | 82 | state++; 83 | break; 84 | } 85 | case State.Note: 86 | { 87 | poLine.POData.Note += line; 88 | 89 | state++; 90 | break; 91 | } 92 | } 93 | } 94 | 95 | return transactionLines; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v3a.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace DoDSamples.Samples 9 | { 10 | public class DoDPOParser_v3a 11 | { 12 | private enum State 13 | { 14 | ID, 15 | DateTime, 16 | Amount, 17 | Note 18 | } 19 | 20 | public POLine_v3[] Parse(string path, out int index) => Parse(File.ReadAllLines(path), out index); 21 | 22 | public POLine_v3[] Parse(string[] lines, out int index) 23 | { 24 | index = 0; 25 | 26 | var transactionLines = new POLine_v3[1000]; 27 | POLine_v3 poLine = new POLine_v3(); 28 | 29 | State state = State.ID; 30 | foreach (var line in lines) 31 | { 32 | if (string.IsNullOrEmpty(line)) 33 | { 34 | if (state >= State.Note) 35 | { 36 | transactionLines[index] = poLine; 37 | state = State.ID; 38 | index++; 39 | } 40 | 41 | continue; 42 | } 43 | switch (state) 44 | { 45 | case State.ID: 46 | { 47 | poLine.POData.ID = int.Parse(line); 48 | 49 | state++; 50 | break; 51 | } 52 | case State.DateTime: 53 | { 54 | var dt = line.Split('T'); 55 | poLine.POData.Date = dt[0]; 56 | poLine.POData.Time = dt[1]; 57 | 58 | state++; 59 | break; 60 | } 61 | case State.Amount: 62 | { 63 | poLine.Amount = double.Parse(line); 64 | 65 | state++; 66 | break; 67 | } 68 | case State.Note: 69 | { 70 | poLine.POData.Note += line; 71 | 72 | state++; 73 | break; 74 | } 75 | } 76 | } 77 | 78 | return transactionLines; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.Intrinsics; 5 | using System.Runtime.Intrinsics.X86; 6 | using System.Text; 7 | 8 | namespace DoDSamples.Samples 9 | { 10 | public struct DoDPOLines_v4 11 | { 12 | public int[] ID; 13 | public double[] Amount; 14 | public string[] Date; 15 | public string[] Time; 16 | public string[] Note; 17 | 18 | public DoDPOLines_v4(int size) 19 | { 20 | ID = new int[size]; 21 | Date = new string[size]; 22 | Time = new string[size]; 23 | Amount = new double[size]; 24 | Note = new string[size]; 25 | } 26 | } 27 | 28 | public class DoDPOParser_v4 29 | { 30 | private enum State 31 | { 32 | ID, 33 | DateTime, 34 | Amount, 35 | Note 36 | } 37 | 38 | public DoDPOLines_v4 Parse(string path, out int index) => Parse(File.ReadAllLines(path), out index); 39 | 40 | public DoDPOLines_v4 Parse(string[] lines, out int index) 41 | { 42 | DoDPOLines_v4 transactionLines = new DoDPOLines_v4(1000); 43 | 44 | index = 0; 45 | State state = State.ID; 46 | foreach (var line in lines) 47 | { 48 | if (string.IsNullOrEmpty(line)) 49 | { 50 | if (state >= State.Note) 51 | { 52 | state = State.ID; 53 | index++; 54 | } 55 | 56 | continue; 57 | } 58 | switch (state) 59 | { 60 | case State.ID: 61 | { 62 | transactionLines.ID[index] = int.Parse(line); 63 | 64 | state++; 65 | break; 66 | } 67 | case State.DateTime: 68 | { 69 | var dt = line.Split('T'); 70 | transactionLines.Date[index] = dt[0]; 71 | transactionLines.Time[index] = dt[1]; 72 | 73 | state++; 74 | break; 75 | } 76 | case State.Amount: 77 | { 78 | transactionLines.Amount[index] = double.Parse(line); 79 | 80 | state++; 81 | break; 82 | } 83 | case State.Note: 84 | { 85 | transactionLines.Note[index] += line; 86 | 87 | state++; 88 | break; 89 | } 90 | } 91 | } 92 | 93 | return transactionLines; 94 | } 95 | 96 | 97 | public unsafe double SumAmount(DoDPOLines_v4 source, int len) 98 | { 99 | double result; 100 | 101 | fixed (double* pSource = source.Amount) 102 | { 103 | Vector128 vresult = Vector128.Zero; 104 | 105 | int i = 0; 106 | int lastBlockIndex = len - (len % 2); 107 | 108 | while (i < lastBlockIndex) 109 | { 110 | vresult = Sse2.Add(vresult, Sse2.LoadVector128(pSource + i)); 111 | i += 2; 112 | } 113 | 114 | vresult = Ssse3.HorizontalAdd(vresult, vresult); 115 | result = vresult.ToScalar(); 116 | 117 | while (i < len) 118 | { 119 | result += pSource[i]; 120 | i += 1; 121 | } 122 | } 123 | 124 | return result; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/DoDPOParser_v5.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DoDSamples.Samples 7 | { 8 | public struct DoDPOLines_v5 9 | { 10 | public int[] ID; 11 | public double[] Amount; 12 | public string[] Date; 13 | public string[] Time; 14 | public int[] NoteOffset; 15 | public StringBuilder AllNotes; 16 | 17 | public DoDPOLines_v5(int size) 18 | { 19 | ID = new int[size]; 20 | Date = new string[size]; 21 | Time = new string[size]; 22 | Amount = new double[size]; 23 | NoteOffset = new int[size]; 24 | AllNotes = new StringBuilder(size); 25 | } 26 | } 27 | 28 | public class DoDPOParser_v5 29 | { 30 | private enum State 31 | { 32 | ID, 33 | DateTime, 34 | Amount, 35 | Note 36 | } 37 | 38 | public DoDPOLines_v5 Parse(string path, out int index) => Parse(File.ReadAllLines(path), out index); 39 | 40 | public DoDPOLines_v5 Parse(string[] lines, out int index) 41 | { 42 | DoDPOLines_v5 transactionLines = new DoDPOLines_v5(1000); 43 | 44 | int offset = 0; 45 | index = 0; 46 | State state = State.ID; 47 | foreach (var line in lines) 48 | { 49 | if (string.IsNullOrEmpty(line)) 50 | { 51 | if (state >= State.Note) 52 | { 53 | state = State.ID; 54 | index++; 55 | } 56 | 57 | continue; 58 | } 59 | switch (state) 60 | { 61 | case State.ID: 62 | { 63 | transactionLines.ID[index] = int.Parse(line); 64 | 65 | state++; 66 | break; 67 | } 68 | case State.DateTime: 69 | { 70 | var dt = line.Split('T'); 71 | transactionLines.Date[index] = dt[0]; 72 | transactionLines.Time[index] = dt[1]; 73 | 74 | state++; 75 | break; 76 | } 77 | case State.Amount: 78 | { 79 | transactionLines.Amount[index] = double.Parse(line); 80 | 81 | state++; 82 | break; 83 | } 84 | case State.Note: 85 | { 86 | offset += line.Length; 87 | 88 | transactionLines.NoteOffset[index] = offset; 89 | transactionLines.AllNotes.Append(line); 90 | 91 | state++; 92 | break; 93 | } 94 | } 95 | } 96 | 97 | return transactionLines; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/OOPParser_Fast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace DoDSamples.Samples 7 | { 8 | public class POLine 9 | { 10 | public int ID { get; set; } 11 | public string Date { get; set; } 12 | public string Time { get; set; } 13 | public double Amount { get; set; } 14 | public string Note { get; set; } 15 | } 16 | 17 | public class POParser_Fast 18 | { 19 | private enum State 20 | { 21 | ID, 22 | DateTime, 23 | Amount, 24 | Note 25 | } 26 | 27 | public List Parse(string path) 28 | { 29 | var lines = File.ReadAllLines(path); 30 | return Parse(lines); 31 | } 32 | 33 | public List Parse(string[] lines) 34 | { 35 | List transactionLines = new List(1000); 36 | POLine poLine = new POLine(); 37 | 38 | State state = State.ID; 39 | foreach (var line in lines) 40 | { 41 | if (string.IsNullOrEmpty(line)) 42 | { 43 | if (state >= State.Note) 44 | { 45 | transactionLines.Add(poLine); 46 | poLine = new POLine(); 47 | state = State.ID; 48 | } 49 | 50 | continue; 51 | } 52 | switch (state) 53 | { 54 | case State.ID: 55 | { 56 | poLine.ID = int.Parse(line); 57 | 58 | state++; 59 | break; 60 | } 61 | case State.DateTime: 62 | { 63 | var dt = line.Split('T'); 64 | poLine.Date = dt[0]; 65 | poLine.Time = dt[1]; 66 | 67 | state++; 68 | break; 69 | } 70 | case State.Amount: 71 | { 72 | poLine.Amount = double.Parse(line); 73 | 74 | state++; 75 | break; 76 | } 77 | case State.Note: 78 | { 79 | poLine.Note += line; 80 | 81 | state++; 82 | break; 83 | } 84 | } 85 | } 86 | 87 | return transactionLines; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /DoDSamples/Samples/POParser/OOPParser_Slow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace DoDSamples.Samples 8 | { 9 | public class POParser 10 | { 11 | private static Regex matchPOLine = 12 | new Regex( 13 | @"(?\d)\r\n(?[0-9]{4}-[0-9]{2}-[0-9]{2})T(?