A game about forced loneliness, made by TACStudios
1using NUnit.Framework; 2using UnityEngine; 3using Unity.Collections.LowLevel.Unsafe; 4using Unity.PerformanceTesting; 5using Unity.PerformanceTesting.Benchmark; 6using System.Runtime.CompilerServices; 7using System.Threading; 8 9namespace Unity.Collections.PerformanceTests 10{ 11 static class ListUtil 12 { 13 static public void AllocInt(ref NativeList<int> container, int capacity, bool addValues) 14 { 15 if (capacity >= 0) 16 { 17 Random.InitState(0); 18 container = new NativeList<int>(capacity, Allocator.Persistent); 19 if (addValues) 20 { 21 for (int i = 0; i < capacity; i++) 22 container.Add(i); 23 } 24 } 25 else 26 container.Dispose(); 27 } 28 static public void AllocInt(ref UnsafeList<int> container, int capacity, bool addValues) 29 { 30 if (capacity >= 0) 31 { 32 Random.InitState(0); 33 container = new UnsafeList<int>(capacity, Allocator.Persistent); 34 if (addValues) 35 { 36 for (int i = 0; i < capacity; i++) 37 container.Add(i); 38 } 39 } 40 else 41 container.Dispose(); 42 } 43 static public object AllocBclContainer(int capacity, bool addValues) 44 { 45 if (capacity < 0) 46 return null; 47 48 Random.InitState(0); 49 var bclContainer = new System.Collections.Generic.List<int>(capacity); 50 if (addValues) 51 { 52 for (int i = 0; i < capacity; i++) 53 bclContainer.Add(i); 54 } 55 return bclContainer; 56 } 57 58 static public void CreateRandomValues(int capacity, ref UnsafeList<int> values) 59 { 60 if (capacity >= 0) 61 { 62 values = new UnsafeList<int>(capacity, Allocator.Persistent); 63 Random.InitState(0); 64 for (int i = 0; i < capacity; i++) 65 { 66 int randKey = Random.Range(0, capacity); 67 values.Add(randKey); 68 } 69 } 70 else 71 values.Dispose(); 72 } 73 } 74 75 struct ListIsEmpty100k : IBenchmarkContainer 76 { 77 const int kIterations = 100_000; 78 NativeList<int> nativeContainer; 79 UnsafeList<int> unsafeContainer; 80 81 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, true); 82 public void AllocUnsafeContainer(int capacity) => ListUtil.AllocInt(ref unsafeContainer, capacity, true); 83 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, true); 84 85 [MethodImpl(MethodImplOptions.NoOptimization)] 86 public void MeasureNativeContainer() 87 { 88 for (int i = 0; i < kIterations; i++) 89 _ = nativeContainer.IsEmpty; 90 } 91 [MethodImpl(MethodImplOptions.NoOptimization)] 92 public void MeasureUnsafeContainer() 93 { 94 for (int i = 0; i < kIterations; i++) 95 _ = unsafeContainer.IsEmpty; 96 } 97 [MethodImpl(MethodImplOptions.NoOptimization)] 98 public void MeasureBclContainer(object container) 99 { 100 var bclContainer = (System.Collections.Generic.List<int>)container; 101 for (int i = 0; i < kIterations; i++) 102 _ = bclContainer.Count == 0; 103 } 104 } 105 106 struct ListCount100k : IBenchmarkContainer 107 { 108 const int kIterations = 100_000; 109 NativeList<int> nativeContainer; 110 UnsafeList<int> unsafeContainer; 111 112 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, true); 113 public void AllocUnsafeContainer(int capacity) => ListUtil.AllocInt(ref unsafeContainer, capacity, true); 114 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, true); 115 116 [MethodImpl(MethodImplOptions.NoOptimization)] 117 public void MeasureNativeContainer() 118 { 119 var reader = nativeContainer.AsReadOnly(); 120 for (int i = 0; i < kIterations; i++) 121 _ = reader.Length; 122 } 123 [MethodImpl(MethodImplOptions.NoOptimization)] 124 public void MeasureUnsafeContainer() 125 { 126 var reader = unsafeContainer.AsReadOnly(); 127 for (int i = 0; i < kIterations; i++) 128 _ = reader.Length; 129 } 130 [MethodImpl(MethodImplOptions.NoOptimization)] 131 public void MeasureBclContainer(object container) 132 { 133 var bclContainer = (System.Collections.Generic.List<int>)container; 134 for (int i = 0; i < kIterations; i++) 135 _ = bclContainer.Count; 136 } 137 } 138 139 struct ListToNativeArray : IBenchmarkContainer 140 { 141 NativeList<int> nativeContainer; 142 143 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, true); 144 public void AllocUnsafeContainer(int capacity) { } 145 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, true); 146 147 public void MeasureNativeContainer() 148 { 149 var asArray = nativeContainer.ToArray(Allocator.Temp); 150 asArray.Dispose(); 151 } 152 public void MeasureUnsafeContainer() { } 153 public void MeasureBclContainer(object container) 154 { 155 var bclContainer = (System.Collections.Generic.List<int>)container; 156 int[] asArray = new int[bclContainer.Count]; 157 bclContainer.CopyTo(asArray, 0); 158 } 159 } 160 161 struct ListAdd : IBenchmarkContainer 162 { 163 int capacity; 164 NativeList<int> nativeContainer; 165 UnsafeList<int> unsafeContainer; 166 167 void IBenchmarkContainer.SetParams(int capacity, params int[] args) => this.capacity = capacity; 168 169 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, false); 170 public void AllocUnsafeContainer(int capacity) => ListUtil.AllocInt(ref unsafeContainer, capacity, false); 171 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, false); 172 173 public void MeasureNativeContainer() 174 { 175 for (int i = 0; i < capacity; i++) 176 nativeContainer.Add(i); 177 } 178 public void MeasureUnsafeContainer() 179 { 180 for (int i = 0; i < capacity; i++) 181 unsafeContainer.Add(i); 182 } 183 public void MeasureBclContainer(object container) 184 { 185 var bclContainer = (System.Collections.Generic.List<int>)container; 186 for (int i = 0; i < capacity; i++) 187 bclContainer.Add(i); 188 } 189 } 190 191 struct ListAddGrow : IBenchmarkContainer 192 { 193 int capacity; 194 int toAdd; 195 NativeList<int> nativeContainer; 196 UnsafeList<int> unsafeContainer; 197 198 void IBenchmarkContainer.SetParams(int capacity, params int[] args) 199 { 200 this.capacity = capacity; 201 toAdd = args[0]; 202 } 203 204 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, true); 205 public void AllocUnsafeContainer(int capacity) => ListUtil.AllocInt(ref unsafeContainer, capacity, true); 206 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, true); 207 208 public void MeasureNativeContainer() 209 { 210 // Intentionally setting capacity small and growing by adding more items 211 for (int i = capacity; i < capacity + toAdd; i++) 212 nativeContainer.Add(i); 213 } 214 public void MeasureUnsafeContainer() 215 { 216 // Intentionally setting capacity small and growing by adding more items 217 for (int i = capacity; i < capacity + toAdd; i++) 218 unsafeContainer.Add(i); 219 } 220 public void MeasureBclContainer(object container) 221 { 222 var bclContainer = (System.Collections.Generic.List<int>)container; 223 // Intentionally setting capacity small and growing by adding more items 224 for (int i = capacity; i < capacity + toAdd; i++) 225 bclContainer.Add(i); 226 } 227 } 228 229 struct ListContains : IBenchmarkContainer 230 { 231 int capacity; 232 NativeList<int> nativeContainer; 233 UnsafeList<int> unsafeContainer; 234 UnsafeList<int> values; 235 236 void IBenchmarkContainer.SetParams(int capacity, params int[] args) => this.capacity = capacity; 237 238 public void AllocNativeContainer(int capacity) 239 { 240 ListUtil.AllocInt(ref nativeContainer, capacity, false); 241 ListUtil.CreateRandomValues(capacity, ref values); 242 for (int i = 0; i < capacity; i++) 243 nativeContainer.Add(values[i]); 244 } 245 public void AllocUnsafeContainer(int capacity) 246 { 247 ListUtil.AllocInt(ref unsafeContainer, capacity, false); 248 ListUtil.CreateRandomValues(capacity, ref values); 249 for (int i = 0; i < capacity; i++) 250 unsafeContainer.Add(values[i]); 251 } 252 public object AllocBclContainer(int capacity) 253 { 254 object container = ListUtil.AllocBclContainer(capacity, false); 255 var bclContainer = (System.Collections.Generic.List<int>)container; 256 ListUtil.CreateRandomValues(capacity, ref values); 257 for (int i = 0; i < capacity; i++) 258 bclContainer.Add(values[i]); 259 return container; 260 } 261 262 public void MeasureNativeContainer() 263 { 264 var reader = nativeContainer.AsReadOnly(); 265 bool data = false; 266 for (int i = 0; i < capacity; i++) 267 Volatile.Write(ref data, reader.Contains(values[i])); 268 } 269 public void MeasureUnsafeContainer() 270 { 271 var reader = unsafeContainer.AsReadOnly(); 272 bool data = false; 273 for (int i = 0; i < capacity; i++) 274 Volatile.Write(ref data, reader.Contains(values[i])); 275 } 276 public void MeasureBclContainer(object container) 277 { 278 var bclContainer = (System.Collections.Generic.List<int>)container; 279 bool data = false; 280 for (int i = 0; i < capacity; i++) 281 Volatile.Write(ref data, bclContainer.Contains(values[i])); 282 } 283 } 284 285 struct ListIndexedRead : IBenchmarkContainer 286 { 287 NativeList<int> nativeContainer; 288 UnsafeList<int> unsafeContainer; 289 UnsafeList<int> values; 290 291 public void AllocNativeContainer(int capacity) 292 { 293 ListUtil.AllocInt(ref nativeContainer, capacity, true); 294 ListUtil.CreateRandomValues(capacity, ref values); 295 } 296 public void AllocUnsafeContainer(int capacity) 297 { 298 ListUtil.AllocInt(ref unsafeContainer, capacity, true); 299 ListUtil.CreateRandomValues(capacity, ref values); 300 } 301 public object AllocBclContainer(int capacity) 302 { 303 object container = ListUtil.AllocBclContainer(capacity, true); 304 ListUtil.CreateRandomValues(capacity, ref values); 305 return container; 306 } 307 308 public void MeasureNativeContainer() 309 { 310 var reader = nativeContainer.AsReadOnly(); 311 int insertions = values.Length; 312 int value = 0; 313 for (int i = 0; i < insertions; i++) 314 Volatile.Write(ref value, reader[values[i]]); 315 } 316 public void MeasureUnsafeContainer() 317 { 318 int insertions = values.Length; 319 int value = 0; 320 for (int i = 0; i < insertions; i++) 321 Volatile.Write(ref value, unsafeContainer[values[i]]); 322 } 323 public void MeasureBclContainer(object container) 324 { 325 var bclContainer = (System.Collections.Generic.List<int>)container; 326 int insertions = values.Length; 327 int value = 0; 328 for (int i = 0; i < insertions; i++) 329 Volatile.Write(ref value, bclContainer[values[i]]); 330 } 331 } 332 333 struct ListIndexedWrite : IBenchmarkContainer 334 { 335 NativeList<int> nativeContainer; 336 UnsafeList<int> unsafeContainer; 337 UnsafeList<int> values; 338 339 public void AllocNativeContainer(int capacity) 340 { 341 ListUtil.AllocInt(ref nativeContainer, capacity, true); 342 ListUtil.CreateRandomValues(capacity, ref values); 343 } 344 public void AllocUnsafeContainer(int capacity) 345 { 346 ListUtil.AllocInt(ref unsafeContainer, capacity, true); 347 ListUtil.CreateRandomValues(capacity, ref values); 348 } 349 public object AllocBclContainer(int capacity) 350 { 351 object container = ListUtil.AllocBclContainer(capacity, true); 352 ListUtil.CreateRandomValues(capacity, ref values); 353 return container; 354 } 355 356 public void MeasureNativeContainer() 357 { 358 int insertions = values.Length; 359 for (int i = 0; i < insertions; i++) 360 nativeContainer[values[i]] = i; 361 } 362 public void MeasureUnsafeContainer() 363 { 364 int insertions = values.Length; 365 for (int i = 0; i < insertions; i++) 366 unsafeContainer[values[i]] = i; 367 } 368 public void MeasureBclContainer(object container) 369 { 370 var bclContainer = (System.Collections.Generic.List<int>)container; 371 int insertions = values.Length; 372 for (int i = 0; i < insertions; i++) 373 bclContainer[values[i]] = i; 374 } 375 } 376 377 struct ListRemove : IBenchmarkContainer 378 { 379 NativeList<int> nativeContainer; 380 UnsafeList<int> unsafeContainer; 381 UnsafeList<int> values; 382 383 void FixValues() 384 { 385 // Ensure if we iterate this list and remove a random index, it will always be a valid index given how many elements still remain. 386 int max = values.Length; 387 while (--max >= 0) 388 { 389 int reverseIndex = values.Length - 1 - max; 390 int value = values[reverseIndex]; 391 if (value > max) 392 values[reverseIndex] = max; 393 } 394 } 395 public void AllocNativeContainer(int capacity) 396 { 397 ListUtil.AllocInt(ref nativeContainer, capacity, true); 398 ListUtil.CreateRandomValues(capacity, ref values); 399 FixValues(); 400 } 401 public void AllocUnsafeContainer(int capacity) 402 { 403 ListUtil.AllocInt(ref unsafeContainer, capacity, true); 404 ListUtil.CreateRandomValues(capacity, ref values); 405 FixValues(); 406 } 407 public object AllocBclContainer(int capacity) 408 { 409 object container = ListUtil.AllocBclContainer(capacity, true); 410 ListUtil.CreateRandomValues(capacity, ref values); 411 FixValues(); 412 return container; 413 } 414 415 public void MeasureNativeContainer() 416 { 417 int insertions = values.Length; 418 for (int i = 0; i < insertions; i++) 419 nativeContainer.RemoveAt(values[i]); 420 } 421 public void MeasureUnsafeContainer() 422 { 423 int insertions = values.Length; 424 for (int i = 0; i < insertions; i++) 425 unsafeContainer.RemoveAt(values[i]); 426 } 427 public void MeasureBclContainer(object container) 428 { 429 var bclContainer = (System.Collections.Generic.List<int>)container; 430 int insertions = values.Length; 431 for (int i = 0; i < insertions; i++) 432 bclContainer.RemoveAt(values[i]); 433 } 434 } 435 436 struct ListForEach : IBenchmarkContainer 437 { 438 NativeList<int> nativeContainer; 439 UnsafeList<int> unsafeContainer; 440 public int total; 441 442 public void AllocNativeContainer(int capacity) => ListUtil.AllocInt(ref nativeContainer, capacity, true); 443 public void AllocUnsafeContainer(int capacity) => ListUtil.AllocInt(ref unsafeContainer, capacity, true); 444 public object AllocBclContainer(int capacity) => ListUtil.AllocBclContainer(capacity, true); 445 446 public void MeasureNativeContainer() 447 { 448 int value = 0; 449 foreach (var element in nativeContainer) 450 Volatile.Write(ref value, element); 451 } 452 public void MeasureUnsafeContainer() 453 { 454 int value = 0; 455 foreach (var element in unsafeContainer) 456 Volatile.Write(ref value, element); 457 } 458 public void MeasureBclContainer(object container) 459 { 460 int value = 0; 461 var bclContainer = (System.Collections.Generic.List<int>)container; 462 foreach (var element in bclContainer) 463 Volatile.Write(ref value, element); 464 } 465 } 466 467 468 [Benchmark(typeof(BenchmarkContainerType))] 469 class List 470 { 471#if UNITY_EDITOR 472 [UnityEditor.MenuItem(BenchmarkContainerConfig.kMenuItemIndividual + nameof(List))] 473 static void RunIndividual() 474 => BenchmarkContainerConfig.RunBenchmark(typeof(List)); 475#endif 476 477 [Test, Performance] 478 [Category("Performance")] 479 public unsafe void IsEmpty_x_100k( 480 [Values(0, 100)] int capacity, 481 [Values] BenchmarkContainerType type) 482 { 483 BenchmarkContainerRunner<ListIsEmpty100k>.Run(capacity, type); 484 } 485 486 [Test, Performance] 487 [Category("Performance")] 488 public unsafe void Count_x_100k( 489 [Values(0, 100)] int capacity, 490 [Values] BenchmarkContainerType type) 491 { 492 BenchmarkContainerRunner<ListCount100k>.Run(capacity, type); 493 } 494 495 [Test, Performance] 496 [Category("Performance")] 497 public unsafe void ToNativeArray( 498 [Values(10000, 100000, 1000000)] int capacity, 499 [Values(BenchmarkContainerType.Native, BenchmarkContainerType.NativeBurstSafety, 500 BenchmarkContainerType.NativeBurstNoSafety)] BenchmarkContainerType type) 501 { 502 BenchmarkContainerRunner<ListToNativeArray>.Run(capacity, type); 503 } 504 505 [Test, Performance] 506 [Category("Performance")] 507 public unsafe void Add( 508 [Values(10000, 100000, 1000000)] int insertions, 509 [Values] BenchmarkContainerType type) 510 { 511 BenchmarkContainerRunner<ListAdd>.Run(insertions, type); 512 } 513 514 [Test, Performance] 515 [Category("Performance")] 516 [BenchmarkTestFootnote("Incrementally grows from `capacity` until reaching size of `growTo`")] 517 public unsafe void AddGrow( 518 [Values(4, 65536)] int capacity, 519 [Values(1024 * 1024)] int growTo, 520 [Values] BenchmarkContainerType type) 521 { 522 BenchmarkContainerRunner<ListAddGrow>.Run(capacity, type, growTo); 523 } 524 525 [Test, Performance] 526 [Category("Performance")] 527 public unsafe void Contains( 528 [Values(1000, 10000)] int insertions, 529 [Values] BenchmarkContainerType type) 530 { 531 BenchmarkContainerRunner<ListContains>.Run(insertions, type); 532 } 533 534 [Test, Performance] 535 [Category("Performance")] 536 public unsafe void IndexedRead( 537 [Values(10000, 100000, 1000000)] int insertions, 538 [Values] BenchmarkContainerType type) 539 { 540 BenchmarkContainerRunner<ListIndexedRead>.Run(insertions, type); 541 } 542 543 [Test, Performance] 544 [Category("Performance")] 545 public unsafe void IndexedWrite( 546 [Values(10000, 100000, 1000000)] int insertions, 547 [Values] BenchmarkContainerType type) 548 { 549 BenchmarkContainerRunner<ListIndexedWrite>.Run(insertions, type); 550 } 551 552 [Test, Performance] 553 [Category("Performance")] 554 public unsafe void Remove( 555 [Values(1000, 10000)] int insertions, 556 [Values] BenchmarkContainerType type) 557 { 558 BenchmarkContainerRunner<ListRemove>.Run(insertions, type); 559 } 560 561 [Test, Performance] 562 [Category("Performance")] 563 public unsafe void Foreach( 564 [Values(10000, 100000, 1000000)] int insertions, 565 [Values] BenchmarkContainerType type) 566 { 567 BenchmarkContainerRunner<ListForEach>.Run(insertions, type); 568 } 569 } 570}