├── .gitattributes ├── .gitignore ├── FastHashSet ├── FastHashSet.cs ├── FastHashSet.csproj └── Properties │ └── AssemblyInfo.cs ├── FastHashSetTestsExtra ├── ExtraTests.cs ├── FastHashSetTestsExtra.csproj ├── FastHashSetTestsExtra.sln └── Properties │ └── AssemblyInfo.cs ├── HashSetBench.sln ├── HashSetBench ├── App.config ├── Bench.cs ├── BenchUtil.cs ├── ClassAndStruct.cs ├── HashSetBench.csproj ├── HashSetBench8.xlsx ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── app.manifest └── packages.config ├── HashSetPerf ├── HashSetClassFast │ ├── App.config │ ├── HashSetClassFast.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── HashSetContains │ ├── App.config │ ├── HashSetContains.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── HashSetContainsC5 │ ├── App.config │ ├── HashSetContainsC5.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── HashSetContainsFast │ ├── App.config │ ├── HashSetContainsFast.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── HashSetPerf.sln ├── HashSetPerf │ ├── App.config │ ├── BenchUtil.cs │ ├── HashSetPerf.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config └── HashSetStructFast │ ├── App.config │ ├── HashSetStructFast.csproj │ ├── Program.cs │ └── Properties │ └── AssemblyInfo.cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true 235 | **/wwwroot/lib/ 236 | 237 | # RIA/Silverlight projects 238 | Generated_Code/ 239 | 240 | # Backup & report files from converting an old project file 241 | # to a newer Visual Studio version. Backup files are not needed, 242 | # because we have git ;-) 243 | _UpgradeReport_Files/ 244 | Backup*/ 245 | UpgradeLog*.XML 246 | UpgradeLog*.htm 247 | ServiceFabricBackup/ 248 | *.rptproj.bak 249 | 250 | # SQL Server files 251 | *.mdf 252 | *.ldf 253 | *.ndf 254 | 255 | # Business Intelligence projects 256 | *.rdl.data 257 | *.bim.layout 258 | *.bim_*.settings 259 | *.rptproj.rsuser 260 | *- Backup*.rdl 261 | 262 | # Microsoft Fakes 263 | FakesAssemblies/ 264 | 265 | # GhostDoc plugin setting file 266 | *.GhostDoc.xml 267 | 268 | # Node.js Tools for Visual Studio 269 | .ntvs_analysis.dat 270 | node_modules/ 271 | 272 | # Visual Studio 6 build log 273 | *.plg 274 | 275 | # Visual Studio 6 workspace options file 276 | *.opt 277 | 278 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 279 | *.vbw 280 | 281 | # Visual Studio LightSwitch build output 282 | **/*.HTMLClient/GeneratedArtifacts 283 | **/*.DesktopClient/GeneratedArtifacts 284 | **/*.DesktopClient/ModelManifest.xml 285 | **/*.Server/GeneratedArtifacts 286 | **/*.Server/ModelManifest.xml 287 | _Pvt_Extensions 288 | 289 | # Paket dependency manager 290 | .paket/paket.exe 291 | paket-files/ 292 | 293 | # FAKE - F# Make 294 | .fake/ 295 | 296 | # JetBrains Rider 297 | .idea/ 298 | *.sln.iml 299 | 300 | # CodeRush personal settings 301 | .cr/personal 302 | 303 | # Python Tools for Visual Studio (PTVS) 304 | __pycache__/ 305 | *.pyc 306 | 307 | # Cake - Uncomment if you are using it 308 | # tools/** 309 | # !tools/packages.config 310 | 311 | # Tabs Studio 312 | *.tss 313 | 314 | # Telerik's JustMock configuration file 315 | *.jmconfig 316 | 317 | # BizTalk build output 318 | *.btp.cs 319 | *.btm.cs 320 | *.odx.cs 321 | *.xsd.cs 322 | 323 | # OpenCover UI analysis results 324 | OpenCover/ 325 | 326 | # Azure Stream Analytics local run output 327 | ASALocalRun/ 328 | 329 | # MSBuild Binary and Structured Log 330 | *.binlog 331 | 332 | # NVidia Nsight GPU debugger configuration file 333 | *.nvuser 334 | 335 | # MFractors (Xamarin productivity tool) working folder 336 | .mfractor/ 337 | 338 | # Local History for Visual Studio 339 | .localhistory/ 340 | 341 | # BeatPulse healthcheck temp database 342 | healthchecksdb -------------------------------------------------------------------------------- /FastHashSet/FastHashSet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55} 8 | Library 9 | Properties 10 | FastHashSet 11 | FastHashSet 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | TRACE;DEBUG 22 | prompt 23 | 4 24 | 7.3 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 7.3 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /FastHashSet/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FastHashSet")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FastHashSet")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a99bfa31-0806-4e83-82cc-c50a0a5d1b55")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | [assembly: InternalsVisibleTo("FastHashSet.Tests")] 38 | [assembly: InternalsVisibleTo("FastHashSet.Explorables")] 39 | 40 | -------------------------------------------------------------------------------- /FastHashSetTestsExtra/ExtraTests.cs: -------------------------------------------------------------------------------- 1 | //#define RunLongTests 2 | //#define RunVeryLongTests 3 | 4 | using Xunit; 5 | using Motvin.Collections; 6 | using System.Text; 7 | using System.Collections.Generic; 8 | using System; 9 | 10 | namespace FastHashSetTestsExtra 11 | { 12 | public class ExtraTests 13 | { 14 | 15 | public static bool IsPrime(int number) 16 | { 17 | if (number <= 2) return false; 18 | if (number % 2 == 0) return (number == 2); 19 | int root = (int)System.Math.Sqrt((double)number); 20 | for (int i = 3; i <= root; i += 2) 21 | { 22 | if (number % i == 0) return false; 23 | } 24 | return true; 25 | } 26 | 27 | [Fact] 28 | public void Prime() 29 | { 30 | int p = FastHashSet.FastHashSetUtil.GetEqualOrClosestHigherPrime(2); 31 | 32 | Assert.True(p == 3); 33 | } 34 | 35 | [Fact] 36 | public void Prime2() 37 | { 38 | for (int i = 2; i < 100_000; i++) 39 | { 40 | int p = FastHashSet.FastHashSetUtil.GetEqualOrClosestHigherPrime(i); 41 | 42 | for (int j = i; j < p; j++) 43 | { 44 | //if (IsPrime(j)) 45 | //{ 46 | // int adf = 1; 47 | //} 48 | Assert.False(IsPrime(j)); 49 | 50 | } 51 | Assert.True(IsPrime(p)); 52 | } 53 | } 54 | 55 | #if RunLongTests 56 | [Fact] 57 | public void Prime3() 58 | { 59 | for (int i = 100_000; i < 10_000_000; i++) 60 | { 61 | int p = FastHashSet.FastHashSetUtil.GetEqualOrClosestHigherPrime(i); 62 | 63 | for (int j = i; j < p; j++) 64 | { 65 | //if (IsPrime(j)) 66 | //{ 67 | // int adf = 1; 68 | //} 69 | Assert.False(IsPrime(j)); 70 | 71 | } 72 | Assert.True(IsPrime(p)); 73 | } 74 | } 75 | #endif 76 | 77 | #if RunVeryLongTests 78 | [Fact] 79 | public void Prime4() 80 | { 81 | for (int i = 10_000_000; i < 100_000_000; i++) 82 | { 83 | int p = FastHashSet.FastHashSetUtil.GetEqualOrClosestHigherPrime(i); 84 | 85 | for (int j = i; j < p; j++) 86 | { 87 | //if (IsPrime(j)) 88 | //{ 89 | // int adf = 1; 90 | //} 91 | Assert.False(IsPrime(j)); 92 | 93 | } 94 | Assert.True(IsPrime(p)); 95 | } 96 | } 97 | #endif 98 | 99 | [Fact] 100 | public void AddIn() 101 | { 102 | FastHashSet set = new FastHashSet(); 103 | int x = 1; 104 | int x2 = 2; 105 | int x3 = 3; 106 | int x4 = 4; 107 | int x5 = 5; 108 | int x6 = 6; 109 | int x7 = 7; 110 | int x8 = 8; 111 | int x9 = 9; 112 | int x10 = 10; 113 | int x11 = 11; 114 | int x12 = 12; 115 | set.Add(in x); 116 | set.Add(in x2); 117 | set.Add(in x3); 118 | set.Add(in x4); 119 | set.Add(in x5); 120 | set.Add(in x6); 121 | set.Add(in x7); 122 | set.Add(in x8); 123 | set.Add(in x9); 124 | set.Add(in x10); 125 | set.Add(in x11); 126 | set.Add(in x12); 127 | 128 | Assert.Contains(1, set); 129 | Assert.True(set.Count == 12); 130 | } 131 | 132 | [Fact] 133 | public void AddIn2() 134 | { 135 | FastHashSet set = new FastHashSet(); 136 | int x = 1; 137 | set.Add(in x); 138 | 139 | Assert.Contains(1, set); 140 | } 141 | 142 | [Fact] 143 | public void ContainsIn() 144 | { 145 | FastHashSet set = new FastHashSet(); 146 | int x = 1; 147 | int x2 = 2; 148 | int x3 = 3; 149 | int x4 = 4; 150 | int x5 = 5; 151 | int x6 = 6; 152 | int x7 = 7; 153 | int x8 = 8; 154 | int x9 = 9; 155 | int x10 = 10; 156 | int x11 = 11; 157 | int x12 = 12; 158 | set.Add(in x); 159 | set.Add(in x2); 160 | set.Add(in x3); 161 | set.Add(in x4); 162 | set.Add(in x5); 163 | set.Add(in x6); 164 | set.Add(in x7); 165 | set.Add(in x8); 166 | set.Add(in x9); 167 | set.Add(in x10); 168 | set.Add(in x11); 169 | set.Add(in x12); 170 | set.Remove(x12); 171 | 172 | Assert.True(set.Contains(in x)); 173 | Assert.True(set.Contains(in x2)); 174 | Assert.True(set.Contains(in x3)); 175 | Assert.True(set.Contains(in x4)); 176 | Assert.True(set.Contains(in x5)); 177 | Assert.True(set.Contains(in x6)); 178 | Assert.True(set.Contains(in x7)); 179 | Assert.True(set.Contains(in x8)); 180 | Assert.True(set.Contains(in x9)); 181 | Assert.True(set.Contains(in x10)); 182 | Assert.True(set.Contains(in x11)); 183 | Assert.False(set.Contains(in x12)); 184 | } 185 | 186 | [Fact] 187 | public void ContainsIn2() 188 | { 189 | FastHashSet set = new FastHashSet(); 190 | int x = 1; 191 | set.Add(in x); 192 | Assert.True(set.Contains(in x)); 193 | } 194 | 195 | [Fact] 196 | public void RemoveIf() 197 | { 198 | FastHashSet set = new FastHashSet(); 199 | int x = 1; 200 | int x2 = 2; 201 | int x3 = 3; 202 | int x4 = 4; 203 | int x5 = 5; 204 | int x6 = 6; 205 | int x7 = 7; 206 | int x8 = 8; 207 | int x9 = 9; 208 | int x10 = 10; 209 | int x11 = 11; 210 | int x12 = 12; 211 | set.Add(in x); 212 | set.Add(in x2); 213 | set.Add(in x3); 214 | set.Add(in x4); 215 | set.Add(in x5); 216 | set.Add(in x6); 217 | set.Add(in x7); 218 | set.Add(in x8); 219 | set.Add(in x9); 220 | set.Add(in x10); 221 | set.Add(in x11); 222 | set.Add(in x12); 223 | 224 | bool isRemoved = set.RemoveIf(in x, n => n < 2); 225 | 226 | Assert.True(isRemoved); 227 | Assert.DoesNotContain(1, set); 228 | 229 | isRemoved = set.RemoveIf(x2, n => n < 2); 230 | 231 | Assert.False(isRemoved); 232 | Assert.Contains(x2, set); 233 | 234 | set.Add(in x); 235 | bool isRemoved2 = set.RemoveIf(in x, n => n > 2); 236 | 237 | Assert.False(isRemoved2); 238 | Assert.Contains(1, set); 239 | } 240 | 241 | [Fact] 242 | public void RemoveIf2() 243 | { 244 | FastHashSet set = new FastHashSet(); 245 | int x = 1; 246 | set.Add(in x); 247 | 248 | bool isRemoved = set.RemoveIf(in x, n => n < 2); 249 | 250 | Assert.True(isRemoved); 251 | Assert.DoesNotContain(1, set); 252 | 253 | set.Add(in x); 254 | bool isRemoved2 = set.RemoveIf(in x, n => n > 2); 255 | 256 | Assert.False(isRemoved2); 257 | Assert.Contains(1, set); 258 | } 259 | 260 | public struct IDCount 261 | { 262 | public int id; 263 | public int count; 264 | 265 | public IDCount(int id, int count) 266 | { 267 | this.id = id; 268 | this.count = count; 269 | } 270 | 271 | public override int GetHashCode() 272 | { 273 | return id; 274 | } 275 | 276 | public override bool Equals(object obj) 277 | { 278 | return this.id == ((IDCount)obj).id; 279 | } 280 | } 281 | 282 | [Fact] 283 | public void FindOrAdd() 284 | { 285 | FastHashSet set = new FastHashSet(); 286 | IDCount x = new IDCount(1, 1); 287 | bool isFound; 288 | ref IDCount xRef = ref set.FindOrAdd(in x, out isFound); 289 | if (isFound) 290 | { 291 | xRef.count++; 292 | } 293 | 294 | bool added = set.Add(new IDCount(1, 1)); // alrady added 295 | Assert.False(added); 296 | 297 | set.Add(new IDCount(2, 1)); 298 | set.Add(new IDCount(3, 1)); 299 | set.Add(new IDCount(4, 1)); 300 | set.Add(new IDCount(6, 1)); 301 | set.Add(new IDCount(7, 1)); 302 | set.Add(new IDCount(8, 1)); 303 | set.Add(new IDCount(9, 1)); 304 | set.Add(new IDCount(10, 1)); 305 | set.Add(new IDCount(11, 1)); 306 | 307 | Assert.False(isFound); 308 | 309 | xRef = ref set.FindOrAdd(in x, out isFound); 310 | if (isFound) 311 | { 312 | xRef.count++; 313 | } 314 | Assert.True(isFound); 315 | Assert.True(xRef.count == 2); 316 | 317 | Assert.Contains(x, set); 318 | } 319 | 320 | [Fact] 321 | public void FindOrAdd2() 322 | { 323 | FastHashSet set = new FastHashSet(); 324 | IDCount x = new IDCount(1, 1); 325 | bool isFound; 326 | ref IDCount xRef = ref set.FindOrAdd(in x, out isFound); 327 | if (isFound) 328 | { 329 | xRef.count++; 330 | } 331 | 332 | Assert.False(isFound); 333 | 334 | xRef = ref set.FindOrAdd(in x, out isFound); 335 | if (isFound) 336 | { 337 | xRef.count++; 338 | } 339 | Assert.True(isFound); 340 | Assert.True(xRef.count == 2); 341 | 342 | Assert.Contains(x, set); 343 | } 344 | 345 | [Fact] 346 | public void Find() 347 | { 348 | FastHashSet set = new FastHashSet(); 349 | IDCount x = new IDCount(1, 123); 350 | set.Add(in x); 351 | set.Add(new IDCount(1, 1)); // already added with 123 as count 352 | set.Add(new IDCount(2, 1)); 353 | set.Add(new IDCount(3, 1)); 354 | set.Add(new IDCount(4, 1)); 355 | set.Add(new IDCount(6, 1)); 356 | set.Add(new IDCount(7, 1)); 357 | set.Add(new IDCount(8, 1)); 358 | set.Add(new IDCount(9, 1)); 359 | set.Add(new IDCount(10, 1)); 360 | set.Add(new IDCount(11, 1)); 361 | bool isFound; 362 | ref IDCount xRef = ref set.Find(in x, out isFound); 363 | if (isFound) 364 | { 365 | xRef.count++; 366 | } 367 | 368 | Assert.True(isFound); 369 | 370 | xRef = ref set.Find(in x, out isFound); 371 | 372 | Assert.True(isFound); 373 | Assert.True(xRef.count == 124); 374 | 375 | Assert.Contains(x, set); 376 | } 377 | 378 | [Fact] 379 | public void ReorderChainedNodesToBeAdjacent() 380 | { 381 | FastHashSet set = new FastHashSet(); 382 | int x = 1; 383 | set.Add(in x); 384 | 385 | Random rand = new Random(17); 386 | int itemCnt = 10_000; 387 | int maxVal = itemCnt * 4; 388 | for (int i = 0; i < itemCnt; i++) 389 | { 390 | set.Add(rand.Next(0, maxVal)); 391 | } 392 | 393 | int cnt = set.Count; 394 | List lst = set.GetChainLevelsCounts(out double avgNodeVisitsPerChain); 395 | set.ReorderChainedNodesToBeAdjacent(); 396 | List lst2 = set.GetChainLevelsCounts(out double avgNodeVisitsPerChain2); 397 | 398 | Assert.Contains(x, set); 399 | Assert.True(set.Count == cnt); 400 | } 401 | 402 | [Fact] 403 | public void Find2() 404 | { 405 | FastHashSet set = new FastHashSet(); 406 | IDCount x = new IDCount(1, 123); 407 | set.Add(in x); 408 | bool isFound; 409 | ref IDCount xRef = ref set.Find(in x, out isFound); 410 | if (isFound) 411 | { 412 | xRef.count++; 413 | } 414 | 415 | Assert.True(isFound); 416 | 417 | xRef = ref set.Find(in x, out isFound); 418 | 419 | Assert.True(isFound); 420 | Assert.True(xRef.count == 124); 421 | 422 | Assert.Contains(x, set); 423 | } 424 | 425 | [Fact] 426 | public void FindAndRemoveIf() 427 | { 428 | FastHashSet set = new FastHashSet(); 429 | IDCount x = new IDCount(5, 1); 430 | 431 | set.Add(new IDCount(1, 1)); 432 | set.Add(new IDCount(2, 1)); 433 | set.Add(new IDCount(3, 1)); 434 | set.Add(new IDCount(4, 1)); 435 | set.Add(new IDCount(6, 1)); 436 | set.Add(new IDCount(7, 1)); 437 | set.Add(new IDCount(8, 1)); 438 | set.Add(new IDCount(9, 1)); 439 | set.Add(new IDCount(10, 1)); 440 | set.Add(new IDCount(11, 1)); 441 | 442 | bool isFound; 443 | bool isRemoved; 444 | 445 | // below is how you can increment a ref counted item and add if the current count = 1 446 | ref IDCount xRef = ref set.FindOrAdd(in x, out isFound); 447 | if (isFound) 448 | { 449 | xRef.count++; 450 | } 451 | Assert.False(isFound); 452 | 453 | xRef = ref set.FindOrAdd(in x, out isFound); 454 | if (isFound) 455 | { 456 | xRef.count++; 457 | } 458 | Assert.True(isFound); 459 | 460 | // below is how you can decrement a ref counted item and remove if the current count = 1 461 | xRef = ref set.FindAndRemoveIf(in x, n => n.count == 1, out isFound, out isRemoved); 462 | if (!isRemoved && isFound) 463 | { 464 | xRef.count--; 465 | } 466 | 467 | Assert.True(isFound); 468 | Assert.False(isRemoved); 469 | 470 | xRef = ref set.FindAndRemoveIf(in x, n => n.count == 1, out isFound, out isRemoved); 471 | if (!isRemoved && isFound) 472 | { 473 | xRef.count--; 474 | } 475 | 476 | Assert.True(isFound); 477 | Assert.True(isRemoved); 478 | 479 | Assert.DoesNotContain(x, set); 480 | } 481 | 482 | [Fact] 483 | public void FindAndRemoveIf2() 484 | { 485 | FastHashSet set = new FastHashSet(); 486 | IDCount x = new IDCount(5, 1); 487 | 488 | set.Add(new IDCount(1, 1)); 489 | set.Add(new IDCount(2, 1)); 490 | set.Add(new IDCount(3, 1)); 491 | 492 | bool isFound; 493 | bool isRemoved; 494 | 495 | // below is how you can increment a ref counted item and add if the current count = 1 496 | ref IDCount xRef = ref set.FindOrAdd(in x, out isFound); 497 | if (isFound) 498 | { 499 | xRef.count++; 500 | } 501 | Assert.False(isFound); 502 | 503 | xRef = ref set.FindOrAdd(in x, out isFound); 504 | if (isFound) 505 | { 506 | xRef.count++; 507 | } 508 | Assert.True(isFound); 509 | 510 | // below is how you can decrement a ref counted item and remove if the current count = 1 511 | xRef = ref set.FindAndRemoveIf(in x, n => n.count == 1, out isFound, out isRemoved); 512 | if (!isRemoved && isFound) 513 | { 514 | xRef.count--; 515 | } 516 | 517 | Assert.True(isFound); 518 | Assert.False(isRemoved); 519 | 520 | xRef = ref set.FindAndRemoveIf(in x, n => n.count == 1, out isFound, out isRemoved); 521 | if (!isRemoved && isFound) 522 | { 523 | xRef.count--; 524 | } 525 | 526 | Assert.True(isFound); 527 | Assert.True(isRemoved); 528 | 529 | Assert.DoesNotContain(x, set); 530 | } 531 | } 532 | } 533 | -------------------------------------------------------------------------------- /FastHashSetTestsExtra/FastHashSetTestsExtra.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BE23D537-9CFB-4711-B7D5-CF22395062DC} 8 | Library 9 | Properties 10 | FastHashSetTestsExtra 11 | FastHashSetTestsExtra 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 50 | FastHashSet 51 | 52 | 53 | 54 | 55 | 2.3.1 56 | runtime; build; native; contentfiles; analyzers; buildtransitive 57 | all 58 | 59 | 60 | 2.4.1 61 | 62 | 63 | 0.10.0 64 | 65 | 66 | 2.4.1 67 | runtime; build; native; contentfiles; analyzers; buildtransitive 68 | all 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /FastHashSetTestsExtra/FastHashSetTestsExtra.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28729.10 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastHashSetTestsExtra", "FastHashSetTestsExtra.csproj", "{BE23D537-9CFB-4711-B7D5-CF22395062DC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastHashSet", "..\FastHashSet\FastHashSet.csproj", "{A99BFA31-0806-4E83-82CC-C50A0A5D1B55}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BE23D537-9CFB-4711-B7D5-CF22395062DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {BE23D537-9CFB-4711-B7D5-CF22395062DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {BE23D537-9CFB-4711-B7D5-CF22395062DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {BE23D537-9CFB-4711-B7D5-CF22395062DC}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {5BECDDB1-8E31-4A57-8462-39F4A114FAC1} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /FastHashSetTestsExtra/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FastHashSetTestsExtra")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FastHashSetTestsExtra")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("be23d537-9cfb-4711-b7d5-cf22395062dc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetBench.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28711.60 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetBench", "HashSetBench\HashSetBench.csproj", "{5BAD01F3-672C-4B32-B04E-7110705892BA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastHashSet", "FastHashSet\FastHashSet.csproj", "{A99BFA31-0806-4E83-82CC-C50A0A5D1B55}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {5BAD01F3-672C-4B32-B04E-7110705892BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {5BAD01F3-672C-4B32-B04E-7110705892BA}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {5BAD01F3-672C-4B32-B04E-7110705892BA}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {5BAD01F3-672C-4B32-B04E-7110705892BA}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B7C52EE0-C4DC-4AB3-AED9-9C9AED6CA4DA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /HashSetBench/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /HashSetBench/BenchUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using SCG = System.Collections.Generic; 7 | using Motvin.Collections; 8 | 9 | namespace HashSetBench 10 | { 11 | public static class BenchUtil 12 | { 13 | public const int MaxCacheSizeInInts = 5_000_000; 14 | 15 | public static int[] clearCacheArray; 16 | public static int[] indicesIntoCacheArray; 17 | 18 | public static int sum; 19 | 20 | public static void ClearCpuCaches() 21 | { 22 | if (clearCacheArray == null) 23 | { 24 | clearCacheArray = new int[MaxCacheSizeInInts * 2]; 25 | 26 | // populate the array 27 | for (int i = 0; i < clearCacheArray.Length; i++) 28 | { 29 | clearCacheArray[i] = 1; 30 | } 31 | 32 | // populate an array of indices into this array and mix up their order 33 | int indicesIntoCacheArraySize = clearCacheArray.Length / 16; // assume that a cache line is at least 16 bytes long 34 | indicesIntoCacheArray = new int[indicesIntoCacheArraySize]; 35 | Random rand = new Random(89); 36 | int maxIdx = indicesIntoCacheArray.Length - 1; 37 | for (int i = 0; i < indicesIntoCacheArraySize; i++) 38 | { 39 | int idx = rand.Next(1, maxIdx); // don't allow 0 index because this will be the not-an-index value 40 | 41 | int j = idx; 42 | for ( ; j < indicesIntoCacheArraySize; j++) 43 | { 44 | if (indicesIntoCacheArray[i] == 0) 45 | { 46 | indicesIntoCacheArray[i] = idx; 47 | break; 48 | } 49 | } 50 | 51 | if (j == indicesIntoCacheArraySize) 52 | { 53 | // try to find a free spot going backwards 54 | j = idx -1; 55 | for ( ; j >= 0; j--) 56 | { 57 | if (indicesIntoCacheArray[i] == 0) 58 | { 59 | indicesIntoCacheArray[i] = idx; 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | for (int i = 0; i < indicesIntoCacheArray.Length; i++) 68 | { 69 | sum += clearCacheArray[indicesIntoCacheArray[i]]; 70 | } 71 | } 72 | 73 | // make sure the minInt/maxInt range is large enough for the length of the array so that the random #'s aren't mostly already taken 74 | public static void PopulateIntArray(int[] dest, Random rand, int minInt, int maxInt, double uniqueValuePercent = 0) 75 | { 76 | if (uniqueValuePercent > 0) 77 | { 78 | int uniqueValuesCount = (int)(uniqueValuePercent * dest.Length); 79 | if (uniqueValuesCount <= 0) 80 | { 81 | uniqueValuesCount = 1; // there must be at least one of these unique values 82 | } 83 | 84 | // first get all unique values in the uniqueValuesArray 85 | HashSet h = new HashSet(); 86 | 87 | int cnt = 0; 88 | if (dest.Length == uniqueValuesCount) 89 | { 90 | while (cnt < uniqueValuesCount) 91 | { 92 | int val = rand.Next(minInt, maxInt); 93 | if (h.Add(val)) 94 | { 95 | dest[cnt] = val; 96 | cnt++; 97 | } 98 | } 99 | } 100 | else 101 | { 102 | int[] uniqueValuesArray = new int[uniqueValuesCount]; 103 | while (cnt < uniqueValuesCount) 104 | { 105 | int val = rand.Next(minInt, maxInt); 106 | if (h.Add(val)) 107 | { 108 | uniqueValuesArray[cnt] = val; 109 | cnt++; 110 | } 111 | } 112 | 113 | PopulateIntArrayFromUniqueArray(dest, rand, uniqueValuesArray, uniqueValuesCount); 114 | } 115 | } 116 | else 117 | { 118 | for (int i = 0; i < dest.Length; i++) 119 | { 120 | dest[i] = rand.Next(minInt, maxInt); 121 | } 122 | } 123 | } 124 | 125 | // put numberOfRandomValues in dest at random places (indices) 126 | // the random values are from minInt to maxInt 127 | public static void PopulateIntArrayAtRandomIndices(int[] dest, Random rand, int minInt, int maxInt, int numberOfRandomValues) 128 | { 129 | for (int i = 0; i < numberOfRandomValues; i++) 130 | { 131 | int val = rand.Next(minInt, maxInt); 132 | int idx = rand.Next(0, dest.Length - 1); 133 | dest[idx] = val; 134 | } 135 | } 136 | 137 | public static void PopulateIntArrayFromUniqueArray(int[] dest, Random rand, int[] uniqueValuesArray, int uniqueValuesCount) 138 | { 139 | // randomly pick an index for each value and indicate that this index is used 140 | bool[] isUsed = new bool[dest.Length]; 141 | 142 | int maxIdx = dest.Length - 1; 143 | int cnt = 0; 144 | while (cnt < uniqueValuesCount) 145 | { 146 | int idx = rand.Next(0, maxIdx); // get a random dest index and place each unique value in these dest index slots 147 | if (!isUsed[idx]) 148 | { 149 | dest[idx] = uniqueValuesArray[cnt]; 150 | isUsed[idx] = true; 151 | cnt++; 152 | } 153 | } 154 | 155 | // now loop through dest and randomly pick a value from uniqueValuesArray - these will be duplicates 156 | maxIdx = uniqueValuesCount - 1; 157 | for (int i = 0; i < dest.Length; i++) 158 | { 159 | if (isUsed[i] == false) 160 | { 161 | int idx = rand.Next(0, maxIdx); 162 | dest[i] = uniqueValuesArray[idx]; 163 | } 164 | } 165 | } 166 | 167 | public static void PopulateCollections25_25_50PctUnique(int maxN, out int[] uniqueArray, out int[] mixedArray, 168 | SCG.HashSet h, FastHashSet f = null, C5.HashSet c5 = null, SCG.SortedSet sortedSet = null, SCG.List lst = null) 169 | { 170 | uniqueArray = new int[maxN]; 171 | mixedArray = new int[maxN]; 172 | 173 | Random rand = new Random(89); 174 | BenchUtil.PopulateIntArray(uniqueArray, rand, int.MinValue, int.MaxValue, 1.0); // a array should have 100% unique values 175 | 176 | int uniqueValuesCount = maxN / 2; // this should produce a c array that has 50% unique values (the other 50% are duplicates), but all are actually in the uniqueArray, so 1, 1, 2, 2 would be an example of this 177 | if (uniqueValuesCount == 0) 178 | { 179 | uniqueValuesCount = 1; 180 | } 181 | BenchUtil.PopulateIntArrayFromUniqueArray(mixedArray, rand, uniqueArray, uniqueValuesCount); 182 | BenchUtil.PopulateIntArrayAtRandomIndices(mixedArray, rand, int.MinValue, int.MaxValue, maxN - uniqueValuesCount); 183 | 184 | if (h != null) 185 | { 186 | for (int i = 0; i < maxN; i++) 187 | { 188 | h.Add(uniqueArray[i]); 189 | } 190 | } 191 | 192 | if (f != null) 193 | { 194 | for (int i = 0; i < maxN; i++) 195 | { 196 | f.Add(uniqueArray[i]); 197 | } 198 | } 199 | 200 | if (c5 != null) 201 | { 202 | for (int i = 0; i < maxN; i++) 203 | { 204 | c5.Add(uniqueArray[i]); 205 | } 206 | } 207 | 208 | if (sortedSet != null) 209 | { 210 | for (int i = 0; i < maxN; i++) 211 | { 212 | sortedSet.Add(uniqueArray[i]); 213 | } 214 | } 215 | 216 | if (lst != null) 217 | { 218 | for (int i = 0; i < maxN; i++) 219 | { 220 | lst.Add(uniqueArray[i]); 221 | } 222 | lst.Sort(); 223 | } 224 | } 225 | } 226 | 227 | public static class StringRandUtil 228 | { 229 | public const string uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 230 | public const string space = " "; 231 | public const string digits = "1234567890"; 232 | public const string lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; 233 | public const string symbols = "!@#$%^&*()_+-=[]{};':\",./<>?\\"; 234 | 235 | public static string CreateRandomString(Random rand, int minLen, int maxLen, string[] freqArray) 236 | { 237 | int len = rand.Next(minLen, maxLen); 238 | 239 | StringBuilder sb = new StringBuilder(new string(' ', len)); 240 | 241 | int maxFreq = freqArray.Length - 1; 242 | string s; 243 | for (int i = 0; i < len; i++) 244 | { 245 | if (maxFreq == 0) 246 | { 247 | s = freqArray[0]; 248 | } 249 | else 250 | { 251 | int freq = rand.Next(0, maxFreq); 252 | s = freqArray[freq]; 253 | } 254 | int n = rand.Next(0, s.Length - 1); 255 | 256 | sb[i] = s[n]; 257 | } 258 | 259 | return sb.ToString(); 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /HashSetBench/ClassAndStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HashSetBench 8 | { 9 | // this should be 8 bytes in size 10 | public struct SmallStruct : IEquatable 11 | { 12 | public int myInt; 13 | public int myInt2; 14 | 15 | public SmallStruct(int i, int i2) 16 | { 17 | myInt = i; 18 | myInt2 = i2; 19 | } 20 | 21 | public static SmallStruct CreateRand(Random rand) 22 | { 23 | int i = rand.Next(); // make this non-negative 24 | int i2 = rand.Next(); // make this non-negative 25 | 26 | return new SmallStruct(i, i2); 27 | } 28 | 29 | public override int GetHashCode() 30 | { 31 | int hash = 13; 32 | 33 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 34 | { 35 | hash = (hash * 7) + myInt; 36 | hash = (hash * 7) + myInt2; 37 | } 38 | 39 | return hash; 40 | } 41 | 42 | public override bool Equals(object obj) 43 | { 44 | if (obj.GetType() != typeof(SmallStruct)) 45 | { 46 | return false; 47 | } 48 | 49 | return Equals((SmallStruct)obj); 50 | } 51 | 52 | public bool Equals(SmallStruct other) 53 | { 54 | return (myInt == other.myInt && myInt2 == other.myInt2); 55 | } 56 | 57 | public static bool operator ==(SmallStruct c1, SmallStruct c2) 58 | { 59 | return c1.Equals(c2); 60 | } 61 | 62 | public static bool operator !=(SmallStruct c1, SmallStruct c2) 63 | { 64 | return !c1.Equals(c2); 65 | } 66 | } 67 | 68 | public sealed class SmallClass : IEquatable 69 | { 70 | public int myInt; 71 | public int myInt2; 72 | 73 | public SmallClass(int i, int i2) 74 | { 75 | myInt = i; 76 | myInt2 = i2; 77 | } 78 | 79 | public static SmallClass CreateRand(Random rand) 80 | { 81 | int i = rand.Next(); // make this non-negative 82 | int i2 = rand.Next(); // make this non-negative 83 | 84 | return new SmallClass(i, i2); 85 | } 86 | 87 | public override int GetHashCode() 88 | { 89 | int hash = 13; 90 | 91 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 92 | { 93 | hash = (hash * 7) + myInt; 94 | hash = (hash * 7) + myInt2; 95 | } 96 | 97 | return hash; 98 | } 99 | 100 | public override bool Equals(object obj) 101 | { 102 | if (obj == null) 103 | { 104 | return false; 105 | } 106 | 107 | return Equals(obj as SmallClass); 108 | } 109 | 110 | public bool Equals(SmallClass other) 111 | { 112 | if (other == null) 113 | { 114 | return false; 115 | } 116 | 117 | return (myInt == other.myInt && myInt2 == other.myInt2); 118 | } 119 | } 120 | 121 | public struct SmallStructIntVal : IEquatable 122 | { 123 | public int myInt; 124 | public int refCountOrSum; 125 | 126 | public SmallStructIntVal(int i, int refCountOrSum) 127 | { 128 | myInt = i; 129 | this.refCountOrSum = refCountOrSum; 130 | } 131 | 132 | public override int GetHashCode() 133 | { 134 | return myInt; 135 | } 136 | 137 | public override bool Equals(object obj) 138 | { 139 | if (obj.GetType() != typeof(SmallStructIntVal)) 140 | { 141 | return false; 142 | } 143 | 144 | return Equals((SmallStruct)obj); 145 | } 146 | 147 | public bool Equals(SmallStructIntVal other) 148 | { 149 | return (myInt == other.myInt); 150 | } 151 | 152 | public static bool operator ==(SmallStructIntVal c1, SmallStructIntVal c2) 153 | { 154 | return c1.Equals(c2); 155 | } 156 | 157 | public static bool operator !=(SmallStructIntVal c1, SmallStructIntVal c2) 158 | { 159 | return !c1.Equals(c2); 160 | } 161 | } 162 | 163 | public sealed class SmallClassIntVal : IEquatable 164 | { 165 | public int myInt; 166 | public int refCountOrSum; 167 | 168 | public SmallClassIntVal(int i, int refCountOrSum) 169 | { 170 | myInt = i; 171 | this.refCountOrSum = refCountOrSum; 172 | } 173 | 174 | public override int GetHashCode() 175 | { 176 | return myInt; 177 | } 178 | 179 | public override bool Equals(object obj) 180 | { 181 | return Equals(obj as SmallClassIntVal); 182 | } 183 | 184 | public bool Equals(SmallClassIntVal other) 185 | { 186 | if (other == null) 187 | { 188 | return false; 189 | } 190 | 191 | return (myInt == other.myInt); 192 | } 193 | } 194 | 195 | // this should be 8 bytes in size 196 | public struct SmallStructBasic 197 | { 198 | public int myInt; 199 | public int myInt2; 200 | 201 | public SmallStructBasic(int i, int i2) 202 | { 203 | myInt = i; 204 | myInt2 = i2; 205 | } 206 | } 207 | 208 | public class SmallClassBasic 209 | { 210 | public int myInt; 211 | public int myInt2; 212 | 213 | public SmallClassBasic(int i, int i2) 214 | { 215 | myInt = i; 216 | myInt2 = i2; 217 | } 218 | } 219 | 220 | // this should be about 20 bytes in size (assuming DateTime is 8 bytes) 221 | public struct MediumStruct : IEquatable 222 | { 223 | public DateTime myDate; 224 | public double myDouble; 225 | public int myInt; 226 | 227 | public MediumStruct(DateTime dt, double d, int i) 228 | { 229 | myDate = dt; 230 | myDouble = d; 231 | myInt = i; 232 | } 233 | 234 | public static MediumStruct CreateRand(Random rand) 235 | { 236 | double d = rand.NextDouble(); 237 | int i = rand.Next(); // make this non-negative 238 | int year = rand.Next(1990, 2019); 239 | int month = rand.Next(1, 12); 240 | int day = rand.Next(1, 28); 241 | 242 | return new MediumStruct(new DateTime(year, month, day), d, i); 243 | } 244 | 245 | public override int GetHashCode() 246 | { 247 | int hash = 13; 248 | 249 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 250 | { 251 | hash = (hash * 7) + myInt; 252 | hash = (hash * 7) + myDouble.GetHashCode(); 253 | hash = (hash * 7) + myDate.GetHashCode(); 254 | } 255 | 256 | return hash; 257 | } 258 | 259 | public override bool Equals(object obj) 260 | { 261 | if (obj.GetType() != typeof(MediumStruct)) 262 | { 263 | return false; 264 | } 265 | 266 | return Equals((MediumStruct)obj); 267 | } 268 | 269 | public bool Equals(MediumStruct other) 270 | { 271 | return (myInt == other.myInt && myDouble == other.myDouble && myDate == other.myDate); 272 | 273 | } 274 | 275 | public static bool operator ==(MediumStruct c1, MediumStruct c2) 276 | { 277 | return c1.Equals(c2); 278 | } 279 | 280 | public static bool operator !=(MediumStruct c1, MediumStruct c2) 281 | { 282 | return !c1.Equals(c2); 283 | } 284 | } 285 | 286 | public sealed class MediumClass : IEquatable 287 | { 288 | public DateTime myDate; 289 | public double myDouble; 290 | public int myInt; 291 | 292 | public MediumClass(DateTime dt, double d, int i) 293 | { 294 | myDate = dt; 295 | myDouble = d; 296 | myInt = i; 297 | } 298 | 299 | public static MediumClass CreateRand(Random rand) 300 | { 301 | double d = rand.NextDouble(); 302 | int i = rand.Next(); // make this non-negative 303 | int year = rand.Next(1990, 2019); 304 | int month = rand.Next(1, 12); 305 | int day = rand.Next(1, 28); 306 | 307 | return new MediumClass(new DateTime(year, month, day), d, i); 308 | } 309 | 310 | public override int GetHashCode() 311 | { 312 | int hash = 13; 313 | 314 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 315 | { 316 | hash = (hash * 7) + myInt; 317 | hash = (hash * 7) + myDouble.GetHashCode(); 318 | hash = (hash * 7) + myDate.GetHashCode(); 319 | } 320 | 321 | return hash; 322 | } 323 | 324 | public override bool Equals(object obj) 325 | { 326 | if (obj == null) 327 | { 328 | return false; 329 | } 330 | 331 | return Equals(obj as MediumClass); 332 | } 333 | 334 | public bool Equals(MediumClass other) 335 | { 336 | if (other == null) 337 | { 338 | return false; 339 | } 340 | 341 | return (myInt == other.myInt && myDouble == other.myDouble && myDate == other.myDate); 342 | } 343 | } 344 | 345 | // this should be about 40 bytes, not including the space for the actual string bytes 346 | public struct LargeStruct : IEquatable 347 | { 348 | public DateTime myDate; 349 | public double myDouble; 350 | public double myDouble2; 351 | public int myInt; 352 | public int myInt2; 353 | public int myInt3; 354 | public string myString; 355 | 356 | public LargeStruct(DateTime dt, double d, double d2, int i, int i2, int i3, string s) 357 | { 358 | myDate = dt; 359 | myDouble = d; 360 | myDouble2 = d2; 361 | myInt = i; 362 | myInt2 = i2; 363 | myInt3 = i3; 364 | myString = s; 365 | } 366 | 367 | public static LargeStruct CreateRand(Random rand) 368 | { 369 | double d = rand.NextDouble(); 370 | double d2 = rand.NextDouble(); 371 | int i = rand.Next(); // make this non-negative 372 | int i2 = rand.Next(); // make this non-negative 373 | int i3 = rand.Next(); // make this non-negative 374 | int year = rand.Next(1990, 2019); 375 | int month = rand.Next(1, 12); 376 | int day = rand.Next(1, 28); 377 | 378 | string[] strFreq = new string[] { 379 | StringRandUtil.uppercaseChars, 380 | StringRandUtil.uppercaseChars, 381 | StringRandUtil.lowercaseChars, 382 | StringRandUtil.lowercaseChars, 383 | StringRandUtil.digits, 384 | StringRandUtil.space, 385 | StringRandUtil.symbols, 386 | }; 387 | string s = StringRandUtil.CreateRandomString(rand, 10, 12, strFreq); 388 | 389 | return new LargeStruct(new DateTime(year, month, day), d, d2, i, i2, i3, s); 390 | } 391 | 392 | public override int GetHashCode() 393 | { 394 | int hash = 13; 395 | 396 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 397 | { 398 | hash = (hash * 7) + myInt; 399 | hash = (hash * 7) + myInt2; 400 | hash = (hash * 7) + myInt3; 401 | hash = (hash * 7) + myDouble.GetHashCode(); 402 | hash = (hash * 7) + myDouble2.GetHashCode(); 403 | hash = (hash * 7) + myDate.GetHashCode(); 404 | hash = (hash * 7) + myString.GetHashCode(); 405 | } 406 | 407 | return hash; 408 | } 409 | 410 | public override bool Equals(object obj) 411 | { 412 | if (obj.GetType() != typeof(LargeStruct)) 413 | { 414 | return false; 415 | } 416 | 417 | return Equals((LargeStruct)obj); 418 | } 419 | 420 | public bool Equals(LargeStruct other) 421 | { 422 | return (myInt == other.myInt && myInt2 == other.myInt2 && myInt3 == other.myInt3 && myDouble == other.myDouble && myDouble2 == other.myDouble2 && myDate == other.myDate && myString == other.myString); 423 | } 424 | 425 | public static bool operator ==(LargeStruct c1, LargeStruct c2) 426 | { 427 | return c1.Equals(c2); 428 | } 429 | 430 | public static bool operator !=(LargeStruct c1, LargeStruct c2) 431 | { 432 | return !c1.Equals(c2); 433 | } 434 | } 435 | 436 | // this should be about 40 bytes, not including the space for the actual string bytes 437 | public sealed class LargeClass : IEquatable 438 | { 439 | public DateTime myDate; 440 | public double myDouble; 441 | public double myDouble2; 442 | public int myInt; 443 | public int myInt2; 444 | public int myInt3; 445 | public string myString; 446 | 447 | public LargeClass(DateTime dt, double d, double d2, int i, int i2, int i3, string s) 448 | { 449 | myDate = dt; 450 | myDouble = d; 451 | myDouble2 = d2; 452 | myInt = i; 453 | myInt2 = i2; 454 | myInt3 = i3; 455 | myString = s; 456 | } 457 | 458 | public static LargeClass CreateRand(Random rand) 459 | { 460 | double d = rand.NextDouble(); 461 | double d2 = rand.NextDouble(); 462 | int i = rand.Next(); // make this non-negative 463 | int i2 = rand.Next(); // make this non-negative 464 | int i3 = rand.Next(); // make this non-negative 465 | int year = rand.Next(1990, 2019); 466 | int month = rand.Next(1, 12); 467 | int day = rand.Next(1, 28); 468 | 469 | string[] strFreq = new string[] { 470 | StringRandUtil.uppercaseChars, 471 | StringRandUtil.uppercaseChars, 472 | StringRandUtil.lowercaseChars, 473 | StringRandUtil.lowercaseChars, 474 | StringRandUtil.digits, 475 | StringRandUtil.space, 476 | StringRandUtil.symbols, 477 | }; 478 | string s = StringRandUtil.CreateRandomString(rand, 10, 12, strFreq); 479 | 480 | return new LargeClass(new DateTime(year, month, day), d, d2, i, i2, i3, s); 481 | } 482 | 483 | public override int GetHashCode() 484 | { 485 | int hash = 13; 486 | 487 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 488 | { 489 | hash = (hash * 7) + myInt; 490 | hash = (hash * 7) + myInt2; 491 | hash = (hash * 7) + myInt3; 492 | hash = (hash * 7) + myDouble.GetHashCode(); 493 | hash = (hash * 7) + myDouble2.GetHashCode(); 494 | hash = (hash * 7) + myDate.GetHashCode(); 495 | hash = (hash * 7) + myString.GetHashCode(); 496 | } 497 | 498 | return hash; 499 | } 500 | 501 | public override bool Equals(object obj) 502 | { 503 | if (obj == null) 504 | { 505 | return false; 506 | } 507 | 508 | return Equals(obj as LargeClass); 509 | } 510 | 511 | public bool Equals(LargeClass other) 512 | { 513 | if (other == null) 514 | { 515 | return false; 516 | } 517 | 518 | return (myInt == other.myInt && myInt2 == other.myInt2 && myInt3 == other.myInt3 && myDouble == other.myDouble && myDouble2 == other.myDouble2 && myDate == other.myDate && myString == other.myString); 519 | } 520 | } 521 | 522 | // this should be about 64 bytes, not including the space for the actual string bytes 523 | public struct VeryLargeStruct : IEquatable 524 | { 525 | public DateTime myDate; 526 | public double myDouble; 527 | public double myDouble2; 528 | public int myInt; 529 | public int myInt2; 530 | public int myInt3; 531 | public string myString; 532 | 533 | public VeryLargeStruct(DateTime dt, double d, double d2, int i, int i2, int i3, string s) 534 | { 535 | myDate = dt; 536 | myDouble = d; 537 | myDouble2 = d2; 538 | myInt = i; 539 | myInt2 = i2; 540 | myInt3 = i3; 541 | myString = s; 542 | } 543 | 544 | public static VeryLargeStruct CreateRand(Random rand) 545 | { 546 | double d = rand.NextDouble(); 547 | double d2 = rand.NextDouble(); 548 | int i = rand.Next(); // make this non-negative 549 | int i2 = rand.Next(); // make this non-negative 550 | int i3 = rand.Next(); // make this non-negative 551 | int year = rand.Next(1990, 2019); 552 | int month = rand.Next(1, 12); 553 | int day = rand.Next(1, 28); 554 | 555 | string[] strFreq = new string[] { 556 | StringRandUtil.uppercaseChars, 557 | StringRandUtil.uppercaseChars, 558 | StringRandUtil.lowercaseChars, 559 | StringRandUtil.lowercaseChars, 560 | StringRandUtil.digits, 561 | StringRandUtil.space, 562 | StringRandUtil.symbols, 563 | }; 564 | string s = StringRandUtil.CreateRandomString(rand, 10, 12, strFreq); 565 | 566 | return new VeryLargeStruct(new DateTime(year, month, day), d, d2, i, i2, i3, s); 567 | } 568 | 569 | public override int GetHashCode() 570 | { 571 | int hash = 13; 572 | 573 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 574 | { 575 | hash = (hash * 7) + myInt; 576 | hash = (hash * 7) + myInt2; 577 | hash = (hash * 7) + myInt3; 578 | hash = (hash * 7) + myDouble.GetHashCode(); 579 | hash = (hash * 7) + myDouble2.GetHashCode(); 580 | hash = (hash * 7) + myDate.GetHashCode(); 581 | hash = (hash * 7) + myString.GetHashCode(); 582 | } 583 | 584 | return hash; 585 | } 586 | 587 | public override bool Equals(object obj) 588 | { 589 | if (obj.GetType() != typeof(VeryLargeStruct)) 590 | { 591 | return false; 592 | } 593 | 594 | return Equals((VeryLargeStruct)obj); 595 | } 596 | 597 | public bool Equals(VeryLargeStruct other) 598 | { 599 | return (myInt == other.myInt && myInt2 == other.myInt2 && myInt3 == other.myInt3 && myDouble == other.myDouble && myDouble2 == other.myDouble2 && myDate == other.myDate && myString == other.myString); 600 | } 601 | 602 | public static bool operator ==(VeryLargeStruct c1, VeryLargeStruct c2) 603 | { 604 | return c1.Equals(c2); 605 | } 606 | 607 | public static bool operator !=(VeryLargeStruct c1, VeryLargeStruct c2) 608 | { 609 | return !c1.Equals(c2); 610 | } 611 | } 612 | 613 | // this should be about 64 bytes, not including the space for the actual string bytes 614 | public sealed class VeryLargeClass : IEquatable 615 | { 616 | public DateTime myDate; 617 | public double myDouble; 618 | public double myDouble2; 619 | public int myInt; 620 | public int myInt2; 621 | public int myInt3; 622 | public string myString; 623 | 624 | public VeryLargeClass(DateTime dt, double d, double d2, int i, int i2, int i3, string s) 625 | { 626 | myDate = dt; 627 | myDouble = d; 628 | myDouble2 = d2; 629 | myInt = i; 630 | myInt2 = i2; 631 | myInt3 = i3; 632 | myString = s; 633 | } 634 | 635 | public static VeryLargeClass CreateRand(Random rand) 636 | { 637 | double d = rand.NextDouble(); 638 | double d2 = rand.NextDouble(); 639 | int i = rand.Next(); // make this non-negative 640 | int i2 = rand.Next(); // make this non-negative 641 | int i3 = rand.Next(); // make this non-negative 642 | int year = rand.Next(1990, 2019); 643 | int month = rand.Next(1, 12); 644 | int day = rand.Next(1, 28); 645 | 646 | string[] strFreq = new string[] { 647 | StringRandUtil.uppercaseChars, 648 | StringRandUtil.uppercaseChars, 649 | StringRandUtil.lowercaseChars, 650 | StringRandUtil.lowercaseChars, 651 | StringRandUtil.digits, 652 | StringRandUtil.space, 653 | StringRandUtil.symbols, 654 | }; 655 | string s = StringRandUtil.CreateRandomString(rand, 10, 12, strFreq); 656 | 657 | return new VeryLargeClass(new DateTime(year, month, day), d, d2, i, i2, i3, s); 658 | } 659 | 660 | public override int GetHashCode() 661 | { 662 | int hash = 13; 663 | 664 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 665 | { 666 | hash = (hash * 7) + myInt; 667 | hash = (hash * 7) + myInt2; 668 | hash = (hash * 7) + myInt3; 669 | hash = (hash * 7) + myDouble.GetHashCode(); 670 | hash = (hash * 7) + myDouble2.GetHashCode(); 671 | hash = (hash * 7) + myDate.GetHashCode(); 672 | hash = (hash * 7) + myString.GetHashCode(); 673 | } 674 | 675 | return hash; 676 | } 677 | 678 | public override bool Equals(object obj) 679 | { 680 | if (obj == null) 681 | { 682 | return false; 683 | } 684 | 685 | return Equals(obj as VeryLargeClass); 686 | } 687 | 688 | public bool Equals(VeryLargeClass other) 689 | { 690 | if (other == null) 691 | { 692 | return false; 693 | } 694 | 695 | return (myInt == other.myInt && myInt2 == other.myInt2 && myInt3 == other.myInt3 && myDouble == other.myDouble && myDouble2 == other.myDouble2 && myDate == other.myDate && myString == other.myString); 696 | } 697 | } 698 | } 699 | -------------------------------------------------------------------------------- /HashSetBench/HashSetBench.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {5BAD01F3-672C-4B32-B04E-7110705892BA} 9 | Exe 10 | HashSetBench 11 | HashSetBench 12 | v4.7.2 13 | 512 14 | true 15 | true 16 | 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 7.3 29 | false 30 | true 31 | 32 | 33 | AnyCPU 34 | pdbonly 35 | true 36 | bin\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | 7.3 41 | false 42 | true 43 | 44 | 45 | app.manifest 46 | 47 | 48 | 49 | False 50 | packages\BenchmarkDotNet.0.11.4\lib\netstandard2.0\BenchmarkDotNet.dll 51 | 52 | 53 | packages\BenchmarkDotNet.Diagnostics.Windows.0.11.4\lib\netstandard2.0\BenchmarkDotNet.Diagnostics.Windows.dll 54 | 55 | 56 | packages\C5.2.5.3\lib\net45\C5.dll 57 | 58 | 59 | packages\CommandLineParser.2.4.3\lib\netstandard2.0\CommandLine.dll 60 | 61 | 62 | packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.34\lib\net45\Dia2Lib.dll 63 | True 64 | 65 | 66 | packages\Microsoft.CodeAnalysis.Common.2.10.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll 67 | 68 | 69 | packages\Microsoft.CodeAnalysis.CSharp.2.10.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll 70 | 71 | 72 | packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.34\lib\net45\Microsoft.Diagnostics.FastSerialization.dll 73 | 74 | 75 | packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.34\lib\net45\Microsoft.Diagnostics.Tracing.TraceEvent.dll 76 | 77 | 78 | packages\Microsoft.DotNet.PlatformAbstractions.2.1.0\lib\net45\Microsoft.DotNet.PlatformAbstractions.dll 79 | 80 | 81 | packages\Microsoft.Win32.Registry.4.5.0\lib\net461\Microsoft.Win32.Registry.dll 82 | 83 | 84 | packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.34\lib\net45\OSExtensions.dll 85 | 86 | 87 | 88 | packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll 89 | True 90 | True 91 | 92 | 93 | packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll 94 | 95 | 96 | 97 | packages\System.Console.4.3.0\lib\net46\System.Console.dll 98 | True 99 | True 100 | 101 | 102 | 103 | packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll 104 | True 105 | True 106 | 107 | 108 | packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll 109 | True 110 | True 111 | 112 | 113 | packages\System.IO.4.3.0\lib\net462\System.IO.dll 114 | True 115 | True 116 | 117 | 118 | packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll 119 | True 120 | True 121 | 122 | 123 | packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll 124 | True 125 | True 126 | 127 | 128 | packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll 129 | True 130 | True 131 | 132 | 133 | packages\System.Linq.4.3.0\lib\net463\System.Linq.dll 134 | True 135 | True 136 | 137 | 138 | packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll 139 | True 140 | True 141 | 142 | 143 | 144 | 145 | packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll 146 | True 147 | True 148 | 149 | 150 | packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll 151 | 152 | 153 | packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll 154 | True 155 | True 156 | 157 | 158 | packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll 159 | 160 | 161 | packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll 162 | True 163 | True 164 | 165 | 166 | packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll 167 | True 168 | True 169 | 170 | 171 | packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 172 | True 173 | True 174 | 175 | 176 | packages\System.Security.AccessControl.4.5.0\lib\net461\System.Security.AccessControl.dll 177 | 178 | 179 | packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll 180 | True 181 | True 182 | 183 | 184 | packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll 185 | True 186 | True 187 | 188 | 189 | packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll 190 | True 191 | True 192 | 193 | 194 | packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll 195 | True 196 | True 197 | 198 | 199 | packages\System.Security.Principal.Windows.4.5.0\lib\net461\System.Security.Principal.Windows.dll 200 | 201 | 202 | packages\System.Text.Encoding.CodePages.4.3.0\lib\net46\System.Text.Encoding.CodePages.dll 203 | 204 | 205 | packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll 206 | 207 | 208 | packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll 209 | True 210 | True 211 | 212 | 213 | packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll 223 | True 224 | True 225 | 226 | 227 | packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll 228 | True 229 | True 230 | 231 | 232 | packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll 233 | True 234 | True 235 | 236 | 237 | packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll 238 | True 239 | True 240 | 241 | 242 | packages\System.Xml.XPath.XmlDocument.4.3.0\lib\net46\System.Xml.XPath.XmlDocument.dll 243 | 244 | 245 | packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.34\lib\net45\TraceReloggerLib.dll 246 | True 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 268 | FastHashSet 269 | 270 | 271 | 272 | 273 | 274 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /HashSetBench/HashSetBench8.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Motvin/FastHashSet/c9d43f6342055e32038be3b441d6758f40d9104f/HashSetBench/HashSetBench8.xlsx -------------------------------------------------------------------------------- /HashSetBench/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using BenchmarkDotNet.Running; 4 | using System.Collections.Generic; 5 | 6 | namespace HashSetBench 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | var summary = BenchmarkRunner.Run(); 13 | //var summary = BenchmarkRunner.Run(); 14 | //var summary2 = BenchmarkRunner.Run(); 15 | //var summary = BenchmarkRunner.Run(); 16 | //var summary = BenchmarkRunner.Run(); 17 | //var summary2 = BenchmarkRunner.Run(); 18 | //var summary = BenchmarkRunner.Run(); 19 | //var summary2 = BenchmarkRunner.Run(); 20 | //var summary3 = BenchmarkRunner.Run(); 21 | //var summar4 = BenchmarkRunner.Run(); 22 | //var summar5 = BenchmarkRunner.Run(); 23 | 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /HashSetBench/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetBench")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetBench")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5bad01f3-672c-4b32-b04e-7110705892ba")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetBench/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 59 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /HashSetBench/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetClassFast/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetClassFast/HashSetClassFast.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {62F10831-1467-4F15-B0A4-CC3D8A23DFEE} 8 | Exe 9 | HashSetClassFast 10 | HashSetClassFast 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 55 | Perf 56 | 57 | 58 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 59 | FastHashSet 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetClassFast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Diagnostics; 7 | using Perf; 8 | using Motvin.Collections; 9 | 10 | 11 | namespace HashSetClassFast 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | // cmd line params variables 18 | string dbConnStr = null; 19 | int runID = 0; 20 | int benchmarkMethodID = 0; 21 | int n; 22 | int maxN; 23 | 24 | try 25 | { 26 | DateTime startTime = DateTime.Now; 27 | //Console.WriteLine($"Args Count:" + args.Length.ToString()); 28 | //foreach (string s in args) 29 | //{ 30 | // Console.WriteLine(s); 31 | //} 32 | //Console.ReadKey(); 33 | 34 | string errMsg = PerfUtil.GetCmdLineParams_DbNAndMaxN(args, out dbConnStr, out runID, out benchmarkMethodID, out n, out maxN); 35 | //if (errMsg != null) 36 | //{ 37 | // Console.WriteLine(errMsg); 38 | //} 39 | //Console.WriteLine($"Args: {dbConnStr}; {runID.ToString()}; {benchmarkMethodID.ToString()}; {n.ToString()}; {maxN.ToString()}"); 40 | //Console.ReadKey(); 41 | 42 | int[] a = new int[n]; 43 | int[] a2 = new int[n]; 44 | 45 | Random rand = new Random(89); 46 | for (int i = 0; i < a.Length; i++) 47 | { 48 | a[i] = rand.Next(); 49 | a2[i] = rand.Next(); 50 | } 51 | 52 | FastHashSet setWarmup = new FastHashSet(); 53 | setWarmup.Add(new SmallClass(1, 2)); 54 | 55 | FastHashSet set = new FastHashSet(); 56 | 57 | double overheadNanoSecs = PerfUtil.GetTimestampOverheadInNanoSeconds(); 58 | 59 | PerfUtil.DoGCCollect(); 60 | 61 | int iterations = 1; 62 | long startTicks; 63 | long endTicks; 64 | double ticks; 65 | 66 | // this is enough to jit things and not put everything in the cache 67 | //bool isContained = set.Contains(0); 68 | 69 | iterations = 1; 70 | 71 | //SmallClass sc = new SmallClass(a[0], a2[0]); 72 | startTicks = Stopwatch.GetTimestamp(); 73 | for (int i = 0; i < a.Length; i++) 74 | { 75 | set.Add(new SmallClass(a[i], a2[i])); 76 | } 77 | // set.Add(sc); 78 | 79 | endTicks = Stopwatch.GetTimestamp(); 80 | 81 | ticks = (double)(endTicks - startTicks); 82 | 83 | double nanoSecs = PerfUtil.GetNanoSecondsFromTicks(ticks, Stopwatch.Frequency) - overheadNanoSecs; 84 | 85 | PerfDb.InsertMeasurement(dbConnStr, runID, benchmarkMethodID, n, iterations, nanoSecs, startTime, DateTime.Now); 86 | 87 | } 88 | catch (Exception ex) 89 | { 90 | Console.Write(ex.ToString()); 91 | if (!string.IsNullOrEmpty(dbConnStr)) 92 | { 93 | // write error to db 94 | PerfDb.InsertRunError(dbConnStr, runID, benchmarkMethodID, ex); 95 | } 96 | else 97 | { 98 | // log error to file 99 | } 100 | } 101 | } 102 | } 103 | public sealed class SmallClass : IEquatable 104 | { 105 | public int myInt; 106 | public int myInt2; 107 | 108 | public SmallClass(int i, int i2) 109 | { 110 | myInt = i; 111 | myInt2 = i2; 112 | } 113 | 114 | public static SmallClass CreateRand(Random rand) 115 | { 116 | int i = rand.Next(); // make this non-negative 117 | int i2 = rand.Next(); // make this non-negative 118 | 119 | return new SmallClass(i, i2); 120 | } 121 | 122 | public override int GetHashCode() 123 | { 124 | int hash = 13; 125 | 126 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 127 | { 128 | hash = (hash * 7) + myInt; 129 | hash = (hash * 7) + myInt2; 130 | } 131 | 132 | return hash; 133 | } 134 | 135 | public override bool Equals(object obj) 136 | { 137 | if (obj == null) 138 | { 139 | return false; 140 | } 141 | 142 | return Equals(obj as SmallClass); 143 | } 144 | 145 | public bool Equals(SmallClass other) 146 | { 147 | if (other == null) 148 | { 149 | return false; 150 | } 151 | 152 | return (myInt == other.myInt && myInt2 == other.myInt2); 153 | } 154 | } 155 | 156 | 157 | } 158 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetClassFast/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetClassFast")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetClassFast")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("62f10831-1467-4f15-b0a4-cc3d8a23dfee")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContains/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContains/HashSetContains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7D8A36A4-D784-4210-B669-DC058EDB138B} 8 | Exe 9 | HashSetContains 10 | HashSetContains 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 57 | Perf 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContains/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Diagnostics; 7 | 8 | using SCG = System.Collections.Generic; 9 | using Perf; 10 | 11 | namespace HashSetContains 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | // cmd line params variables 18 | string dbConnStr = null; 19 | int runID = 0; 20 | int benchmarkMethodID = 0; 21 | int n; 22 | int maxN; 23 | 24 | try 25 | { 26 | DateTime startTime = DateTime.Now; 27 | //Console.WriteLine($"Args Count:" + args.Length.ToString()); 28 | //foreach (string s in args) 29 | //{ 30 | // Console.WriteLine(s); 31 | //} 32 | //Console.ReadKey(); 33 | 34 | string errMsg = PerfUtil.GetCmdLineParams_DbNAndMaxN(args, out dbConnStr, out runID, out benchmarkMethodID, out n, out maxN); 35 | //if (errMsg != null) 36 | //{ 37 | // Console.WriteLine(errMsg); 38 | //} 39 | //Console.WriteLine($"Args: {dbConnStr}; {runID.ToString()}; {benchmarkMethodID.ToString()}; {n.ToString()}; {maxN.ToString()}"); 40 | //Console.ReadKey(); 41 | 42 | int[] a; 43 | int[] c; 44 | 45 | HashSet set = new HashSet(); 46 | 47 | BenchUtil.PopulateArrays25_25_50PctUnique(maxN, out a, out c); 48 | 49 | // in a real world scenario, we will have probably have recently added the items into the set, so no need to try to clear the cache or anything 50 | for (int i = 0; i < maxN; i++) 51 | { 52 | set.Add(a[i]); 53 | } 54 | 55 | double overheadNanoSecs = PerfUtil.GetTimestampOverheadInNanoSeconds(); 56 | 57 | PerfUtil.DoGCCollect(); 58 | 59 | int iterations = 1; 60 | long startTicks; 61 | long endTicks; 62 | double ticks; 63 | 64 | // this is enough to jit things and not put everything in the cache 65 | bool isContained = set.Contains(0); 66 | 67 | if (maxN <= 1000) 68 | { 69 | iterations = 1; 70 | 71 | // there amount of time taken for these is too small to measure just one iteration - so we measure multiple iterations in a loop and get the time for these 72 | // the mean time is this total time / iterations 73 | 74 | // for really small operations, like a single contains on a hashet that has 8 items you would probably want to call at least a feww hundred Contains 75 | // and then average that - the maxN wouldn't work too well if that was just 8 76 | 77 | startTicks = Stopwatch.GetTimestamp(); 78 | 79 | for (int i = 0; i < maxN; i++) 80 | { 81 | set.Contains(c[i]); 82 | } 83 | 84 | endTicks = Stopwatch.GetTimestamp(); 85 | 86 | ticks = ((endTicks - startTicks) * n) / (double)maxN; 87 | } 88 | else 89 | { 90 | iterations = 1; 91 | 92 | startTicks = Stopwatch.GetTimestamp(); 93 | for (int i = 0; i < n; i++) // loop overhead is ok because we assume there will be some loop in real-world scenario 94 | { 95 | set.Contains(c[i]); 96 | } 97 | endTicks = Stopwatch.GetTimestamp(); 98 | 99 | ticks = (double)(endTicks - startTicks); 100 | } 101 | 102 | double nanoSecs = PerfUtil.GetNanoSecondsFromTicks(ticks, Stopwatch.Frequency) - overheadNanoSecs; 103 | 104 | PerfDb.InsertMeasurement(dbConnStr, runID, benchmarkMethodID, n, iterations, nanoSecs, startTime, DateTime.Now); 105 | } 106 | catch (Exception ex) 107 | { 108 | Console.Write(ex.ToString()); 109 | if (!string.IsNullOrEmpty(dbConnStr)) 110 | { 111 | // write error to db 112 | PerfDb.InsertRunError(dbConnStr, runID, benchmarkMethodID, ex); 113 | } 114 | else 115 | { 116 | // log error to file 117 | } 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContains/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetContains")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetContains")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7d8a36a4-d784-4210-b669-dc058edb138b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsC5/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsC5/HashSetContainsC5.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {74C21F9D-D981-474B-893F-64DE177CF895} 8 | Exe 9 | HashSetContainsC5 10 | HashSetContainsC5 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\C5.2.5.3\lib\net45\C5.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 60 | Perf 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsC5/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | //using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Diagnostics; 5 | using Perf; 6 | 7 | namespace HashSetContainsC5 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | // cmd line params variables 14 | string dbConnStr = null; 15 | int runID = 0; 16 | int benchmarkMethodID = 0; 17 | int n; 18 | int maxN; 19 | 20 | try 21 | { 22 | DateTime startTime = DateTime.Now; 23 | //Console.WriteLine($"Args Count:" + args.Length.ToString()); 24 | //foreach (string s in args) 25 | //{ 26 | // Console.WriteLine(s); 27 | //} 28 | //Console.ReadKey(); 29 | 30 | string errMsg = PerfUtil.GetCmdLineParams_DbNAndMaxN(args, out dbConnStr, out runID, out benchmarkMethodID, out n, out maxN); 31 | //if (errMsg != null) 32 | //{ 33 | // Console.WriteLine(errMsg); 34 | //} 35 | //Console.WriteLine($"Args: {dbConnStr}; {runID.ToString()}; {benchmarkMethodID.ToString()}; {n.ToString()}; {maxN.ToString()}"); 36 | //Console.ReadKey(); 37 | 38 | int[] a; 39 | int[] c; 40 | 41 | C5.HashSet set = new C5.HashSet(); 42 | 43 | BenchUtil.PopulateArrays25_25_50PctUnique(maxN, out a, out c); 44 | 45 | // in a real world scenario, we will have probably have recently added the items into the set, so no need to try to clear the cache or anything 46 | for (int i = 0; i < maxN; i++) 47 | { 48 | set.Add(a[i]); 49 | } 50 | 51 | double overheadNanoSecs = PerfUtil.GetTimestampOverheadInNanoSeconds(); 52 | 53 | PerfUtil.DoGCCollect(); 54 | 55 | int iterations = 1; 56 | long startTicks; 57 | long endTicks; 58 | double ticks; 59 | 60 | // this is enough to jit things and not put everything in the cache 61 | bool isContained = set.Contains(0); 62 | 63 | if (maxN <= 1000) 64 | { 65 | iterations = 1; 66 | 67 | // there amount of time taken for these is too small to measure just one iteration - so we measure multiple iterations in a loop and get the time for these 68 | // the mean time is this total time / iterations 69 | 70 | startTicks = Stopwatch.GetTimestamp(); 71 | 72 | for (int i = 0; i < maxN; i++) 73 | { 74 | set.Contains(c[i]); 75 | } 76 | 77 | endTicks = Stopwatch.GetTimestamp(); 78 | 79 | ticks = ((endTicks - startTicks) * n) / (double)maxN; 80 | } 81 | else 82 | { 83 | iterations = 1; 84 | 85 | startTicks = Stopwatch.GetTimestamp(); 86 | for (int i = 0; i < n; i++) // loop overhead is ok because we assume there will be some loop in real-world scenario 87 | { 88 | set.Contains(c[i]); 89 | } 90 | endTicks = Stopwatch.GetTimestamp(); 91 | 92 | ticks = (double)(endTicks - startTicks); 93 | } 94 | 95 | double nanoSecs = PerfUtil.GetNanoSecondsFromTicks(ticks, Stopwatch.Frequency) - overheadNanoSecs; 96 | 97 | PerfDb.InsertMeasurement(dbConnStr, runID, benchmarkMethodID, n, iterations, nanoSecs, startTime, DateTime.Now); 98 | } 99 | catch (Exception ex) 100 | { 101 | Console.Write(ex.ToString()); 102 | if (!string.IsNullOrEmpty(dbConnStr)) 103 | { 104 | // write error to db 105 | PerfDb.InsertRunError(dbConnStr, runID, benchmarkMethodID, ex); 106 | } 107 | else 108 | { 109 | // log error to file 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsC5/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetContainsC5")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetContainsC5")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("74c21f9d-d981-474b-893f-64de177cf895")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsFast/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsFast/HashSetContainsFast.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {39E71982-201B-43A3-98EF-C9E78F252498} 8 | Exe 9 | HashSetContainsFast 10 | HashSetContainsFast 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 57 | Perf 58 | 59 | 60 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 61 | FastHashSet 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsFast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using Perf; 5 | using Motvin.Collections; 6 | 7 | namespace HashSetContainsFast 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | // cmd line params variables 14 | string dbConnStr = null; 15 | int runID = 0; 16 | int benchmarkMethodID = 0; 17 | int n; 18 | int maxN; 19 | 20 | try 21 | { 22 | DateTime startTime = DateTime.Now; 23 | //Console.WriteLine($"Args Count:" + args.Length.ToString()); 24 | //foreach (string s in args) 25 | //{ 26 | // Console.WriteLine(s); 27 | //} 28 | //Console.ReadKey(); 29 | 30 | string errMsg = PerfUtil.GetCmdLineParams_DbNAndMaxN(args, out dbConnStr, out runID, out benchmarkMethodID, out n, out maxN); 31 | //if (errMsg != null) 32 | //{ 33 | // Console.WriteLine(errMsg); 34 | //} 35 | //Console.WriteLine($"Args: {dbConnStr}; {runID.ToString()}; {benchmarkMethodID.ToString()}; {n.ToString()}; {maxN.ToString()}"); 36 | //Console.ReadKey(); 37 | 38 | int[] a; 39 | int[] c; 40 | 41 | FastHashSet set = new FastHashSet(); 42 | 43 | BenchUtil.PopulateArrays25_25_50PctUnique(maxN, out a, out c); 44 | 45 | // in a real world scenario, we will have probably have recently added the items into the set, so no need to try to clear the cache or anything 46 | for (int i = 0; i < maxN; i++) 47 | { 48 | set.Add(a[i]); 49 | } 50 | 51 | double overheadNanoSecs = PerfUtil.GetTimestampOverheadInNanoSeconds(); 52 | 53 | PerfUtil.DoGCCollect(); 54 | 55 | int iterations = 1; 56 | long startTicks; 57 | long endTicks; 58 | double ticks; 59 | 60 | // this is enough to jit things and not put everything in the cache 61 | bool isContained = set.Contains(0); 62 | 63 | if (maxN <= 1000) 64 | { 65 | iterations = 1; 66 | 67 | // there amount of time taken for these is too small to measure just one iteration - so we measure multiple iterations in a loop and get the time for these 68 | // the mean time is this total time / iterations 69 | 70 | startTicks = Stopwatch.GetTimestamp(); 71 | 72 | for (int i = 0; i < maxN; i++) 73 | { 74 | set.Contains(c[i]); 75 | } 76 | 77 | endTicks = Stopwatch.GetTimestamp(); 78 | 79 | ticks = ((endTicks - startTicks) * n) / (double)maxN; 80 | } 81 | else 82 | { 83 | iterations = 1; 84 | 85 | startTicks = Stopwatch.GetTimestamp(); 86 | for (int i = 0; i < n; i++) // loop overhead is ok because we assume there will be some loop in real-world scenario 87 | { 88 | set.Contains(c[i]); 89 | } 90 | endTicks = Stopwatch.GetTimestamp(); 91 | 92 | ticks = (double)(endTicks - startTicks); 93 | } 94 | 95 | double nanoSecs = PerfUtil.GetNanoSecondsFromTicks(ticks, Stopwatch.Frequency) - overheadNanoSecs; 96 | 97 | PerfDb.InsertMeasurement(dbConnStr, runID, benchmarkMethodID, n, iterations, nanoSecs, startTime, DateTime.Now); 98 | 99 | } 100 | catch (Exception ex) 101 | { 102 | Console.Write(ex.ToString()); 103 | if (!string.IsNullOrEmpty(dbConnStr)) 104 | { 105 | // write error to db 106 | PerfDb.InsertRunError(dbConnStr, runID, benchmarkMethodID, ex); 107 | } 108 | else 109 | { 110 | // log error to file 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetContainsFast/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetContainsFast")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetContainsFast")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("39e71982-201b-43a3-98ef-c9e78f252498")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28714.193 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetPerf", "HashSetPerf\HashSetPerf.csproj", "{F46BDD49-599A-4158-9578-228B7868F1EB}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastHashSet", "..\FastHashSet\FastHashSet.csproj", "{A99BFA31-0806-4E83-82CC-C50A0A5D1B55}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetContainsC5", "HashSetContainsC5\HashSetContainsC5.csproj", "{74C21F9D-D981-474B-893F-64DE177CF895}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetContains", "HashSetContains\HashSetContains.csproj", "{7D8A36A4-D784-4210-B669-DC058EDB138B}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetContainsFast", "HashSetContainsFast\HashSetContainsFast.csproj", "{39E71982-201B-43A3-98EF-C9E78F252498}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perf", "..\..\Perf\Perf.csproj", "{BE6AECF7-2469-4AF6-9CDD-11F4E52387C7}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetClassFast", "HashSetClassFast\HashSetClassFast.csproj", "{62F10831-1467-4F15-B0A4-CC3D8A23DFEE}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSetStructFast", "HashSetStructFast\HashSetStructFast.csproj", "{BBB3A2E6-0774-429D-AE19-016A63B265B0}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {F46BDD49-599A-4158-9578-228B7868F1EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {F46BDD49-599A-4158-9578-228B7868F1EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {F46BDD49-599A-4158-9578-228B7868F1EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {F46BDD49-599A-4158-9578-228B7868F1EB}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {A99BFA31-0806-4E83-82CC-C50A0A5D1B55}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {74C21F9D-D981-474B-893F-64DE177CF895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {74C21F9D-D981-474B-893F-64DE177CF895}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {74C21F9D-D981-474B-893F-64DE177CF895}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {74C21F9D-D981-474B-893F-64DE177CF895}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {7D8A36A4-D784-4210-B669-DC058EDB138B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {7D8A36A4-D784-4210-B669-DC058EDB138B}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {7D8A36A4-D784-4210-B669-DC058EDB138B}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {7D8A36A4-D784-4210-B669-DC058EDB138B}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {39E71982-201B-43A3-98EF-C9E78F252498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {39E71982-201B-43A3-98EF-C9E78F252498}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {39E71982-201B-43A3-98EF-C9E78F252498}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {39E71982-201B-43A3-98EF-C9E78F252498}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {BE6AECF7-2469-4AF6-9CDD-11F4E52387C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {BE6AECF7-2469-4AF6-9CDD-11F4E52387C7}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {BE6AECF7-2469-4AF6-9CDD-11F4E52387C7}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {BE6AECF7-2469-4AF6-9CDD-11F4E52387C7}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {62F10831-1467-4F15-B0A4-CC3D8A23DFEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {62F10831-1467-4F15-B0A4-CC3D8A23DFEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {62F10831-1467-4F15-B0A4-CC3D8A23DFEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {62F10831-1467-4F15-B0A4-CC3D8A23DFEE}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {BBB3A2E6-0774-429D-AE19-016A63B265B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {BBB3A2E6-0774-429D-AE19-016A63B265B0}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {BBB3A2E6-0774-429D-AE19-016A63B265B0}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {BBB3A2E6-0774-429D-AE19-016A63B265B0}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(ExtensibilityGlobals) = postSolution 65 | SolutionGuid = {BA876230-63F0-4219-B505-5778CF7AB54E} 66 | EndGlobalSection 67 | EndGlobal 68 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/BenchUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using System.Collections.Generic; 6 | using Motvin.Collections; 7 | 8 | namespace HashSetBench 9 | { 10 | public static class BenchUtil 11 | { 12 | public const int MaxCacheSizeInInts = 5_000_000; 13 | 14 | public static int[] clearCacheArray; 15 | public static int[] indicesIntoCacheArray; 16 | 17 | public static int sum; 18 | 19 | public static void ClearCpuCaches() 20 | { 21 | if (clearCacheArray == null) 22 | { 23 | clearCacheArray = new int[MaxCacheSizeInInts * 2]; 24 | 25 | // populate the array 26 | for (int i = 0; i < clearCacheArray.Length; i++) 27 | { 28 | clearCacheArray[i] = 1; 29 | } 30 | 31 | // populate an array of indices into this array and mix up their order 32 | int indicesIntoCacheArraySize = clearCacheArray.Length / 16; // assume that a cache line is at least 16 bytes long 33 | indicesIntoCacheArray = new int[indicesIntoCacheArraySize]; 34 | Random rand = new Random(89); 35 | int maxIdx = indicesIntoCacheArray.Length - 1; 36 | for (int i = 0; i < indicesIntoCacheArraySize; i++) 37 | { 38 | int idx = rand.Next(1, maxIdx); // don't allow 0 index because this will be the not-an-index value 39 | 40 | int j = idx; 41 | for ( ; j < indicesIntoCacheArraySize; j++) 42 | { 43 | if (indicesIntoCacheArray[i] == 0) 44 | { 45 | indicesIntoCacheArray[i] = idx; 46 | break; 47 | } 48 | } 49 | 50 | if (j == indicesIntoCacheArraySize) 51 | { 52 | // try to find a free spot going backwards 53 | j = idx -1; 54 | for ( ; j >= 0; j--) 55 | { 56 | if (indicesIntoCacheArray[i] == 0) 57 | { 58 | indicesIntoCacheArray[i] = idx; 59 | break; 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | for (int i = 0; i < indicesIntoCacheArray.Length; i++) 67 | { 68 | sum += clearCacheArray[indicesIntoCacheArray[i]]; 69 | } 70 | } 71 | 72 | // make sure the minInt/maxInt range is large enough for the length of the array so that the random #'s aren't mostly already taken 73 | public static void PopulateIntArray(int[] dest, Random rand, int minInt, int maxInt, double uniqueValuePercent = 0) 74 | { 75 | if (uniqueValuePercent > 0) 76 | { 77 | int uniqueValuesCount = (int)(uniqueValuePercent * dest.Length); 78 | if (uniqueValuesCount <= 0) 79 | { 80 | uniqueValuesCount = 1; // there must be at least one of these unique values 81 | } 82 | 83 | // first get all unique values in the uniqueValuesArray 84 | HashSet h = new HashSet(); 85 | 86 | int cnt = 0; 87 | if (dest.Length == uniqueValuesCount) 88 | { 89 | while (cnt < uniqueValuesCount) 90 | { 91 | int val = rand.Next(minInt, maxInt); 92 | if (h.Add(val)) 93 | { 94 | dest[cnt] = val; 95 | cnt++; 96 | } 97 | } 98 | } 99 | else 100 | { 101 | int[] uniqueValuesArray = new int[uniqueValuesCount]; 102 | while (cnt < uniqueValuesCount) 103 | { 104 | int val = rand.Next(minInt, maxInt); 105 | if (h.Add(val)) 106 | { 107 | uniqueValuesArray[cnt] = val; 108 | cnt++; 109 | } 110 | } 111 | 112 | PopulateIntArrayFromUniqueArray(dest, rand, uniqueValuesArray, uniqueValuesCount); 113 | } 114 | } 115 | else 116 | { 117 | for (int i = 0; i < dest.Length; i++) 118 | { 119 | dest[i] = rand.Next(minInt, maxInt); 120 | } 121 | } 122 | } 123 | 124 | // put numberOfRandomValues in dest at random places (indices) 125 | // the random values are from minInt to maxInt 126 | public static void PopulateIntArrayAtRandomIndices(int[] dest, Random rand, int minInt, int maxInt, int numberOfRandomValues) 127 | { 128 | for (int i = 0; i < numberOfRandomValues; i++) 129 | { 130 | int val = rand.Next(minInt, maxInt); 131 | int idx = rand.Next(0, dest.Length - 1); 132 | dest[idx] = val; 133 | } 134 | } 135 | 136 | public static void PopulateIntArrayFromUniqueArray(int[] dest, Random rand, int[] uniqueValuesArray, int uniqueValuesCount) 137 | { 138 | // randomly pick an index for each value and indicate that this index is used 139 | bool[] isUsed = new bool[dest.Length]; 140 | 141 | int maxIdx = dest.Length - 1; 142 | int cnt = 0; 143 | while (cnt < uniqueValuesCount) 144 | { 145 | int idx = rand.Next(0, maxIdx); // get a random dest index and place each unique value in these dest index slots 146 | if (!isUsed[idx]) 147 | { 148 | dest[idx] = uniqueValuesArray[cnt]; 149 | isUsed[idx] = true; 150 | cnt++; 151 | } 152 | } 153 | 154 | // now loop through dest and randomly pick a value from uniqueValuesArray - these will be duplicates 155 | maxIdx = uniqueValuesCount - 1; 156 | for (int i = 0; i < dest.Length; i++) 157 | { 158 | if (isUsed[i] == false) 159 | { 160 | int idx = rand.Next(0, maxIdx); 161 | dest[i] = uniqueValuesArray[idx]; 162 | } 163 | } 164 | } 165 | 166 | public static void PopulateCollections25_25_50PctUnique(int maxN, out int[] uniqueArray, out int[] mixedArray, 167 | HashSet h, FastHashSet f = null, C5.HashSet c5 = null, SortedSet sortedSet = null, List lst = null) 168 | { 169 | uniqueArray = new int[maxN]; 170 | mixedArray = new int[maxN]; 171 | 172 | Random rand = new Random(89); 173 | BenchUtil.PopulateIntArray(uniqueArray, rand, int.MinValue, int.MaxValue, 1.0); // a array should have 100% unique values 174 | 175 | int uniqueValuesCount = maxN / 2; // this should produce a c array that has 50% unique values (the other 50% are duplicates), but all are actually in the uniqueArray, so 1, 1, 2, 2 would be an example of this 176 | if (uniqueValuesCount == 0) 177 | { 178 | uniqueValuesCount = 1; 179 | } 180 | BenchUtil.PopulateIntArrayFromUniqueArray(mixedArray, rand, uniqueArray, uniqueValuesCount); 181 | BenchUtil.PopulateIntArrayAtRandomIndices(mixedArray, rand, int.MinValue, int.MaxValue, maxN - uniqueValuesCount); 182 | 183 | if (h != null) 184 | { 185 | for (int i = 0; i < maxN; i++) 186 | { 187 | h.Add(uniqueArray[i]); 188 | } 189 | } 190 | 191 | if (f != null) 192 | { 193 | for (int i = 0; i < maxN; i++) 194 | { 195 | f.Add(uniqueArray[i]); 196 | } 197 | } 198 | 199 | if (c5 != null) 200 | { 201 | for (int i = 0; i < maxN; i++) 202 | { 203 | c5.Add(uniqueArray[i]); 204 | } 205 | } 206 | 207 | if (sortedSet != null) 208 | { 209 | for (int i = 0; i < maxN; i++) 210 | { 211 | sortedSet.Add(uniqueArray[i]); 212 | } 213 | } 214 | 215 | if (lst != null) 216 | { 217 | for (int i = 0; i < maxN; i++) 218 | { 219 | lst.Add(uniqueArray[i]); 220 | } 221 | lst.Sort(); 222 | } 223 | } 224 | } 225 | 226 | public static class StringRandUtil 227 | { 228 | public const string uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 229 | public const string space = " "; 230 | public const string digits = "1234567890"; 231 | public const string lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; 232 | public const string symbols = "!@#$%^&*()_+-=[]{};':\",./<>?\\"; 233 | 234 | public static string CreateRandomString(Random rand, int minLen, int maxLen, string[] freqArray) 235 | { 236 | int len = rand.Next(minLen, maxLen); 237 | 238 | StringBuilder sb = new StringBuilder(new string(' ', len)); 239 | 240 | int maxFreq = freqArray.Length - 1; 241 | string s; 242 | for (int i = 0; i < len; i++) 243 | { 244 | if (maxFreq == 0) 245 | { 246 | s = freqArray[0]; 247 | } 248 | else 249 | { 250 | int freq = rand.Next(0, maxFreq); 251 | s = freqArray[freq]; 252 | } 253 | int n = rand.Next(0, s.Length - 1); 254 | 255 | sb[i] = s[n]; 256 | } 257 | 258 | return sb.ToString(); 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/HashSetPerf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F46BDD49-599A-4158-9578-228B7868F1EB} 8 | Exe 9 | HashSetPerf 10 | HashSetPerf 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\C5.2.5.3\lib\net45\C5.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 62 | Perf 63 | 64 | 65 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 66 | FastHashSet 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | using System.Runtime.CompilerServices; 7 | 8 | using SCG = System.Collections.Generic; 9 | using Motvin.Collections; 10 | using Perf; 11 | 12 | namespace HashSetPerf 13 | { 14 | class Program 15 | { 16 | static void Main(string[] args) 17 | { 18 | //string outputFileName = @"e:\\proj\\summary.tsv"; 19 | //int minN = 100_000; 20 | //int maxN = 1_000_000; 21 | 22 | //int incrementNBy = 10_000; 23 | string errMsg = PerfUtil.GetCmdLineParams_OutputFileAndMinMaxIncN(args, out int minN, out int maxN, out int incrementNBy, out string outputFileName); 24 | 25 | int nCount = ((maxN - minN) / incrementNBy) + 1; 26 | int[] nArray = new int[nCount]; 27 | 28 | int idx = 0; 29 | for (int n = minN; n <= maxN; n += incrementNBy, idx++) 30 | { 31 | nArray[idx] = n; 32 | } 33 | 34 | const int LoopUnrollCount = 1; 35 | const int IterartionCount = 512; 36 | const int IterartionWarmupCount = 16; 37 | 38 | long [] ticksH = new long[nArray.Length * IterartionCount * LoopUnrollCount]; 39 | int ticksIdxForH = 0; 40 | 41 | long [] ticksF = new long[nArray.Length * IterartionCount * LoopUnrollCount]; 42 | int ticksIdxForF = 0; 43 | 44 | long [] ticksC = new long[nArray.Length * IterartionCount * LoopUnrollCount]; 45 | int ticksIdxForC = 0; 46 | 47 | long startTicks; 48 | 49 | double overheadTicks = PerfUtil.GetTimestampOverheadInNanoSeconds(); 50 | 51 | int[] a; 52 | int[] c; 53 | 54 | SCG.HashSet h = new HashSet(); 55 | FastHashSet f = new FastHashSet(); 56 | C5.HashSet c5 = new C5.HashSet(); 57 | 58 | HashSetBench.BenchUtil.PopulateCollections25_25_50PctUnique(maxN, out a, out c, h, f, c5); 59 | 60 | // not sure if we should run bechmark 1 and then benchmark 2 separately so that the presence of the one doesn't effect the other??? 61 | // in practice they will probably not be run together one after the other 62 | 63 | PerfUtil.DoGCCollect(); 64 | 65 | int N; 66 | for (int j = 0; j < nArray.Length; j++) 67 | { 68 | N = nArray[j]; 69 | 70 | // not really sure what running the warmup really does - it can put things in the cache that maybe shouldn't be there because they won't be in a real application??? 71 | // still, if we execute the same code with the same data in a loop alot of times, this will populate the cache unrealistically 72 | // also if we do a warmup, the jit times will be removed, but does this represent reality - jit times do happen in real running code??? 73 | 74 | for (int iterationIdx = 0; iterationIdx < IterartionWarmupCount; iterationIdx++) 75 | { 76 | // SCG_Contains 77 | for (int i = 0; i < N; i++) 78 | { 79 | h.Contains(c[i]); 80 | } 81 | 82 | // Fast_Contains 83 | for (int i = 0; i < N; i++) 84 | { 85 | f.Contains(c[i]); 86 | } 87 | 88 | for (int i = 0; i < N; i++) 89 | { 90 | c5.Contains(c[i]); 91 | } 92 | } 93 | 94 | for (int iterationIdx = 0; iterationIdx < IterartionCount; iterationIdx++) 95 | { 96 | // to minimize the effects of the loop code on the count, unroll each bechmark 2 times 97 | // also alternate randomly between the order of these to minimize any effects of order 98 | // not sure what effects loop unrolling has since that part isn't contained in the stopwatch time 99 | // still there might be some residual effects on CPU registers? - not really sure 100 | 101 | // 1 102 | 103 | // there is some overhead that should be removed - it is returning from GetTimestamp and setting startTicks and afterwards calling GetTimestamp until the point where the return value is obtained 104 | // we should determine this overhead by calling 105 | startTicks = Stopwatch.GetTimestamp(); 106 | for (int i = 0; i < N; i++) 107 | { 108 | h.Contains(c[i]); 109 | } 110 | ticksH[ticksIdxForH++] = Stopwatch.GetTimestamp() - startTicks; 111 | 112 | startTicks = Stopwatch.GetTimestamp(); 113 | for (int i = 0; i < N; i++) 114 | { 115 | f.Contains(c[i]); 116 | } 117 | ticksF[ticksIdxForF++] = Stopwatch.GetTimestamp() - startTicks; 118 | 119 | startTicks = Stopwatch.GetTimestamp(); 120 | for (int i = 0; i < N; i++) 121 | { 122 | c5.Contains(c[i]); 123 | } 124 | ticksC[ticksIdxForC++] = Stopwatch.GetTimestamp() - startTicks; 125 | } 126 | } 127 | 128 | // summarize and output the data 129 | 130 | BenchmarkSummaries summaries = new BenchmarkSummaries(); 131 | 132 | summaries.AddNSummaryList(NSummary.CreateNSummaryListForBenchmark(overheadTicks, nArray, IterartionCount * LoopUnrollCount, ticksH), "SCG_Contains"); 133 | 134 | summaries.AddNSummaryList(NSummary.CreateNSummaryListForBenchmark(overheadTicks, nArray, IterartionCount * LoopUnrollCount, ticksF), "Fast_Contains"); 135 | 136 | summaries.AddNSummaryList(NSummary.CreateNSummaryListForBenchmark(overheadTicks, nArray, IterartionCount * LoopUnrollCount, ticksC), "C5_Contains"); 137 | 138 | summaries.OutputSummariesToFile(outputFileName, "SCG_Contains"); 139 | } 140 | } 141 | 142 | public static class Perf 143 | { 144 | } 145 | 146 | 147 | // public class Timings 148 | // { 149 | //private long[] timingsArray; 150 | //private int timingsIdx; 151 | 152 | //[MethodImpl(MethodImplOptions.AggressiveInlining)] //??? not sure this should be used 153 | //public Timings(int timingsCount) 154 | //{ 155 | // timingsArray = new long[timingsCount]; 156 | //} 157 | 158 | //[MethodImpl(MethodImplOptions.AggressiveInlining)] 159 | //public void Start(Stopwatch sw) 160 | //{ 161 | // sw.Restart(); 162 | //} 163 | 164 | //[MethodImpl(MethodImplOptions.AggressiveInlining)] 165 | //public void Stop(Stopwatch sw, bool getElapsedTime = true) 166 | //{ 167 | // sw.Stop(); 168 | // if (getElapsedTime) 169 | // { 170 | // timingsArray[timingsIdx++] = sw.ElapsedTicks; 171 | // } 172 | //} 173 | 174 | //[MethodImpl(MethodImplOptions.AggressiveInlining)] 175 | //public void StopAndRestart(Stopwatch sw, bool getElapsedTime = true) 176 | //{ 177 | // if (getElapsedTime) 178 | // { 179 | // sw.Stop(); 180 | // timingsArray[timingsIdx++] = sw.ElapsedTicks; 181 | // } 182 | // sw.Restart(); 183 | //} 184 | 185 | //public void Clear() 186 | //{ 187 | // Array 188 | //} 189 | 190 | //public static void WriteSummaryToFile(string fileName, Timings[] timimngsPerBechmarkArray, string[] benchmarkNameArray) 191 | //{ 192 | // foreach () 193 | // perfArray 194 | // long ticksPerSec = Stopwatch.Frequency; 195 | // long nanoSecPerTick = (1000L * 1000L * 1000L) / ticksPerSec; 196 | 197 | //} 198 | // } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetPerf")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetPerf")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f46bdd49-599a-4158-9578-228b7868f1eb")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetPerf/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetStructFast/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetStructFast/HashSetStructFast.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BBB3A2E6-0774-429D-AE19-016A63B265B0} 8 | Exe 9 | HashSetStructFast 10 | HashSetStructFast 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {be6aecf7-2469-4af6-9cdd-11f4e52387c7} 55 | Perf 56 | 57 | 58 | {a99bfa31-0806-4e83-82cc-c50a0a5d1b55} 59 | FastHashSet 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetStructFast/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Diagnostics; 5 | using Perf; 6 | using Motvin.Collections; 7 | 8 | namespace HashSetStructFast 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | // cmd line params variables 15 | string dbConnStr = null; 16 | int runID = 0; 17 | int benchmarkMethodID = 0; 18 | int n; 19 | int maxN; 20 | 21 | try 22 | { 23 | DateTime startTime = DateTime.Now; 24 | //Console.WriteLine($"Args Count:" + args.Length.ToString()); 25 | //foreach (string s in args) 26 | //{ 27 | // Console.WriteLine(s); 28 | //} 29 | //Console.ReadKey(); 30 | 31 | string errMsg = PerfUtil.GetCmdLineParams_DbNAndMaxN(args, out dbConnStr, out runID, out benchmarkMethodID, out n, out maxN); 32 | //if (errMsg != null) 33 | //{ 34 | // Console.WriteLine(errMsg); 35 | //} 36 | //Console.WriteLine($"Args: {dbConnStr}; {runID.ToString()}; {benchmarkMethodID.ToString()}; {n.ToString()}; {maxN.ToString()}"); 37 | //Console.ReadKey(); 38 | 39 | int[] a = new int[n]; 40 | int[] a2 = new int[n]; 41 | 42 | Random rand = new Random(89); 43 | for (int i = 0; i < a.Length; i++) 44 | { 45 | a[i] = rand.Next(); 46 | a2[i] = rand.Next(); 47 | } 48 | 49 | FastHashSet setWarmup = new FastHashSet(); 50 | setWarmup.Add(new SmallStruct(1, 2)); 51 | 52 | FastHashSet set = new FastHashSet(); 53 | 54 | double overheadNanoSecs = PerfUtil.GetTimestampOverheadInNanoSeconds(); 55 | 56 | PerfUtil.DoGCCollect(); 57 | 58 | int iterations = 1; 59 | long startTicks; 60 | long endTicks; 61 | double ticks; 62 | 63 | // this is enough to jit things and not put everything in the cache 64 | //bool isContained = set.Contains(0); 65 | 66 | iterations = 1; 67 | 68 | startTicks = Stopwatch.GetTimestamp(); 69 | for (int i = 0; i < a.Length; i++) 70 | { 71 | set.Add(new SmallStruct(a[i], a2[i])); 72 | } 73 | 74 | endTicks = Stopwatch.GetTimestamp(); 75 | 76 | ticks = (double)(endTicks - startTicks); 77 | 78 | double nanoSecs = PerfUtil.GetNanoSecondsFromTicks(ticks, Stopwatch.Frequency) - overheadNanoSecs; 79 | 80 | PerfDb.InsertMeasurement(dbConnStr, runID, benchmarkMethodID, n, iterations, nanoSecs, startTime, DateTime.Now); 81 | 82 | } 83 | catch (Exception ex) 84 | { 85 | Console.Write(ex.ToString()); 86 | if (!string.IsNullOrEmpty(dbConnStr)) 87 | { 88 | // write error to db 89 | PerfDb.InsertRunError(dbConnStr, runID, benchmarkMethodID, ex); 90 | } 91 | else 92 | { 93 | // log error to file 94 | } 95 | } 96 | } 97 | } 98 | // this should be 8 bytes in size 99 | public struct SmallStruct : IEquatable 100 | { 101 | public int myInt; 102 | public int myInt2; 103 | 104 | public SmallStruct(int i, int i2) 105 | { 106 | myInt = i; 107 | myInt2 = i2; 108 | } 109 | 110 | public static SmallStruct CreateRand(Random rand) 111 | { 112 | int i = rand.Next(); // make this non-negative 113 | int i2 = rand.Next(); // make this non-negative 114 | 115 | return new SmallStruct(i, i2); 116 | } 117 | 118 | public override int GetHashCode() 119 | { 120 | int hash = 13; 121 | 122 | unchecked // the code below may overflow the hash int and that will cause an exception if compiler is checking for arithmetic overflow - unchecked prevents this 123 | { 124 | hash = (hash * 7) + myInt; 125 | hash = (hash * 7) + myInt2; 126 | } 127 | 128 | return hash; 129 | } 130 | 131 | public override bool Equals(object obj) 132 | { 133 | if (obj.GetType() != typeof(SmallStruct)) 134 | { 135 | return false; 136 | } 137 | 138 | return Equals((SmallStruct)obj); 139 | } 140 | 141 | public bool Equals(SmallStruct other) 142 | { 143 | return (myInt == other.myInt && myInt2 == other.myInt2); 144 | } 145 | 146 | public static bool operator ==(SmallStruct c1, SmallStruct c2) 147 | { 148 | return c1.Equals(c2); 149 | } 150 | 151 | public static bool operator !=(SmallStruct c1, SmallStruct c2) 152 | { 153 | return !c1.Equals(c2); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /HashSetPerf/HashSetStructFast/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HashSetStructFast")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("HashSetStructFast")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bbb3a2e6-0774-429d-ae19-016a63b265b0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastHashSet 2 | FastHashSet is a replacement for the generic HashSet<T>. It is usually faster, has more predictable memory allocations, and uses C# 7.x ref returns and readonly ref params (in). 3 | --------------------------------------------------------------------------------