No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NativeParallelHashSetTests.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. using System;
  2. using NUnit.Framework;
  3. using Unity.Burst;
  4. using Unity.Collections;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. using Unity.Collections.NotBurstCompatible;
  7. using Unity.Jobs;
  8. using Unity.Collections.Tests;
  9. internal class NativeParallelHashSetTests: CollectionsTestFixture
  10. {
  11. static void ExpectedCount<T>(ref NativeParallelHashSet<T> container, int expected)
  12. where T : unmanaged, IEquatable<T>
  13. {
  14. Assert.AreEqual(expected == 0, container.IsEmpty);
  15. Assert.AreEqual(expected, container.Count());
  16. }
  17. [Test]
  18. public void NativeParallelHashSet_IsEmpty()
  19. {
  20. var container = new NativeParallelHashSet<int>(0, Allocator.Persistent);
  21. Assert.IsTrue(container.IsEmpty);
  22. Assert.IsTrue(container.Add(0));
  23. Assert.IsFalse(container.IsEmpty);
  24. Assert.AreEqual(1, container.Capacity);
  25. ExpectedCount(ref container, 1);
  26. container.Remove(0);
  27. Assert.IsTrue(container.IsEmpty);
  28. Assert.IsTrue(container.Add(0));
  29. container.Clear();
  30. Assert.IsTrue(container.IsEmpty);
  31. container.Dispose();
  32. }
  33. [Test]
  34. public void UnsafeParallelHashSet_Capacity()
  35. {
  36. var container = new NativeParallelHashSet<int>(0, Allocator.Persistent);
  37. Assert.IsTrue(container.IsEmpty);
  38. Assert.AreEqual(0, container.Capacity);
  39. container.Capacity = 10;
  40. Assert.AreEqual(10, container.Capacity);
  41. container.Dispose();
  42. }
  43. [Test]
  44. [TestRequiresCollectionChecks]
  45. public void NativeParallelHashSet_Full_Throws()
  46. {
  47. var container = new NativeParallelHashSet<int>(16, Allocator.Temp);
  48. ExpectedCount(ref container, 0);
  49. for (int i = 0, capacity = container.Capacity; i < capacity; ++i)
  50. {
  51. Assert.DoesNotThrow(() => { container.Add(i); });
  52. }
  53. ExpectedCount(ref container, container.Capacity);
  54. // Make sure overallocating throws and exception if using the Concurrent version - normal hash map would grow
  55. var writer = container.AsParallelWriter();
  56. Assert.Throws<System.InvalidOperationException>(() => { writer.Add(100); });
  57. ExpectedCount(ref container, container.Capacity);
  58. container.Clear();
  59. ExpectedCount(ref container, 0);
  60. container.Dispose();
  61. }
  62. [Test]
  63. public void NativeParallelHashSet_RemoveOnEmptyMap_DoesNotThrow()
  64. {
  65. var container = new NativeParallelHashSet<int>(0, Allocator.Temp);
  66. Assert.DoesNotThrow(() => container.Remove(0));
  67. Assert.DoesNotThrow(() => container.Remove(-425196));
  68. container.Dispose();
  69. }
  70. [Test]
  71. public void NativeParallelHashSet_Collisions()
  72. {
  73. var container = new NativeParallelHashSet<int>(16, Allocator.Temp);
  74. Assert.IsFalse(container.Contains(0), "Contains on empty hash map did not fail");
  75. ExpectedCount(ref container, 0);
  76. // Make sure inserting values work
  77. for (int i = 0; i < 8; ++i)
  78. {
  79. Assert.IsTrue(container.Add(i), "Failed to add value");
  80. }
  81. ExpectedCount(ref container, 8);
  82. // The bucket size is capacity * 2, adding that number should result in hash collisions
  83. for (int i = 0; i < 8; ++i)
  84. {
  85. Assert.IsTrue(container.Add(i + 32), "Failed to add value with potential hash collision");
  86. }
  87. // Make sure reading the inserted values work
  88. for (int i = 0; i < 8; ++i)
  89. {
  90. Assert.IsTrue(container.Contains(i), "Failed get value from hash set");
  91. }
  92. for (int i = 0; i < 8; ++i)
  93. {
  94. Assert.IsTrue(container.Contains(i + 32), "Failed get value from hash set");
  95. }
  96. container.Dispose();
  97. }
  98. [Test]
  99. public void NativeParallelHashSet_SameElement()
  100. {
  101. using (var container = new NativeParallelHashSet<int>(0, Allocator.Persistent))
  102. {
  103. Assert.IsTrue(container.Add(0));
  104. Assert.IsFalse(container.Add(0));
  105. }
  106. }
  107. [Test]
  108. public void NativeParallelHashSet_ParallelWriter_CanBeUsedInJob()
  109. {
  110. const int count = 32;
  111. using (var hashSet = new NativeParallelHashSet<int>(count, CommonRwdAllocator.Handle))
  112. {
  113. new ParallelWriteToHashSetJob
  114. {
  115. Writer = hashSet.AsParallelWriter()
  116. }.Schedule(count, 2).Complete();
  117. var result = hashSet.ToNativeArray(Allocator.Temp);
  118. result.Sort();
  119. for (int i = 0; i < count; i++)
  120. Assert.AreEqual(i, result[i]);
  121. }
  122. }
  123. struct ParallelWriteToHashSetJob : IJobParallelFor
  124. {
  125. [WriteOnly]
  126. public NativeParallelHashSet<int>.ParallelWriter Writer;
  127. public void Execute(int index)
  128. {
  129. Writer.Add(index);
  130. }
  131. }
  132. [Test]
  133. public void NativeParallelHashSet_CanBeReadFromJob()
  134. {
  135. using (var hashSet = new NativeParallelHashSet<int>(1, CommonRwdAllocator.Handle))
  136. using (var result = new NativeReference<int>(CommonRwdAllocator.Handle))
  137. {
  138. hashSet.Add(42);
  139. new ReadHashSetJob
  140. {
  141. Input = hashSet.AsReadOnly(),
  142. Output = result,
  143. }.Run();
  144. Assert.AreEqual(42, result.Value);
  145. }
  146. }
  147. struct TempHashSet : IJob
  148. {
  149. public void Execute()
  150. {
  151. using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" })
  152. {
  153. var container = new NativeParallelHashSet<FixedString128Bytes>(50, Allocator.Temp);
  154. var seen = new NativeArray<int>(stringList.Length, Allocator.Temp);
  155. foreach (var str in stringList)
  156. {
  157. container.Add(str);
  158. }
  159. foreach (var value in container)
  160. {
  161. int index = stringList.IndexOf(value);
  162. Assert.AreEqual(stringList[index], value.ToString());
  163. seen[index] = seen[index] + 1;
  164. }
  165. for (int i = 0; i < stringList.Length; i++)
  166. {
  167. Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}");
  168. }
  169. }
  170. }
  171. }
  172. [Test]
  173. public void NativeParallelHashSet_TempHashSetInJob()
  174. {
  175. new TempHashSet { }.Schedule().Complete();
  176. }
  177. struct ReadHashSetJob : IJob
  178. {
  179. [ReadOnly]
  180. public NativeParallelHashSet<int>.ReadOnly Input;
  181. public NativeReference<int> Output;
  182. public void Execute()
  183. {
  184. Output.Value = Input.ToNativeArray(Allocator.Temp)[0];
  185. foreach (var value in Input)
  186. {
  187. Assert.AreEqual(42, value);
  188. }
  189. }
  190. }
  191. [Test]
  192. public void NativeParallelHashSet_ForEach_FixedStringInHashMap()
  193. {
  194. using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" })
  195. {
  196. var container = new NativeParallelHashSet<FixedString128Bytes>(50, Allocator.Temp);
  197. var seen = new NativeArray<int>(stringList.Length, Allocator.Temp);
  198. foreach (var str in stringList)
  199. {
  200. container.Add(str);
  201. }
  202. foreach (var value in container)
  203. {
  204. int index = stringList.IndexOf(value);
  205. Assert.AreEqual(stringList[index], value.ToString());
  206. seen[index] = seen[index] + 1;
  207. }
  208. for (int i = 0; i < stringList.Length; i++)
  209. {
  210. Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}");
  211. }
  212. }
  213. }
  214. [Test]
  215. public void NativeParallelHashSet_ForEach([Values(10, 1000)]int n)
  216. {
  217. var seen = new NativeArray<int>(n, Allocator.Temp);
  218. using (var container = new NativeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
  219. {
  220. for (int i = 0; i < n; i++)
  221. {
  222. container.Add(i);
  223. }
  224. var count = 0;
  225. foreach (var item in container)
  226. {
  227. Assert.True(container.Contains(item));
  228. seen[item] = seen[item] + 1;
  229. ++count;
  230. }
  231. Assert.AreEqual(container.Count(), count);
  232. for (int i = 0; i < n; i++)
  233. {
  234. Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
  235. }
  236. }
  237. }
  238. struct NativeParallelHashSet_ForEach_Job : IJob
  239. {
  240. [ReadOnly]
  241. public NativeParallelHashSet<int>.ReadOnly Input;
  242. [ReadOnly]
  243. public int Num;
  244. public void Execute()
  245. {
  246. var seen = new NativeArray<int>(Num, Allocator.Temp);
  247. var count = 0;
  248. foreach (var item in Input)
  249. {
  250. Assert.True(Input.Contains(item));
  251. seen[item] = seen[item] + 1;
  252. ++count;
  253. }
  254. Assert.AreEqual(Input.Count(), count);
  255. for (int i = 0; i < Num; i++)
  256. {
  257. Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
  258. }
  259. seen.Dispose();
  260. }
  261. }
  262. [Test]
  263. public void NativeParallelHashSet_ForEach_From_Job([Values(10, 1000)] int n)
  264. {
  265. using (var container = new NativeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
  266. {
  267. for (int i = 0; i < n; i++)
  268. {
  269. container.Add(i);
  270. }
  271. new NativeParallelHashSet_ForEach_Job
  272. {
  273. Input = container.AsReadOnly(),
  274. Num = n,
  275. }.Run();
  276. }
  277. }
  278. [Test]
  279. [TestRequiresCollectionChecks]
  280. public void NativeParallelHashSet_ForEach_Throws_When_Modified()
  281. {
  282. using (var container = new NativeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
  283. {
  284. container.Add(0);
  285. container.Add(1);
  286. container.Add(2);
  287. container.Add(3);
  288. container.Add(4);
  289. container.Add(5);
  290. container.Add(6);
  291. container.Add(7);
  292. container.Add(8);
  293. container.Add(9);
  294. Assert.Throws<ObjectDisposedException>(() =>
  295. {
  296. foreach (var item in container)
  297. {
  298. container.Add(10);
  299. }
  300. });
  301. Assert.Throws<ObjectDisposedException>(() =>
  302. {
  303. foreach (var item in container)
  304. {
  305. container.Remove(1);
  306. }
  307. });
  308. }
  309. }
  310. [Test]
  311. [TestRequiresCollectionChecks]
  312. public void NativeParallelHashSet_ForEach_Throws()
  313. {
  314. using (var container = new NativeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
  315. {
  316. var iter = container.GetEnumerator();
  317. var jobHandle = new ParallelWriteToHashSetJob
  318. {
  319. Writer = container.AsParallelWriter()
  320. }.Schedule(1, 2);
  321. Assert.Throws<ObjectDisposedException>(() =>
  322. {
  323. while (iter.MoveNext())
  324. {
  325. }
  326. });
  327. jobHandle.Complete();
  328. }
  329. }
  330. struct ForEachIterator : IJob
  331. {
  332. [ReadOnly]
  333. public NativeParallelHashSet<int>.Enumerator Iter;
  334. public void Execute()
  335. {
  336. while (Iter.MoveNext())
  337. {
  338. }
  339. }
  340. }
  341. [Test]
  342. [TestRequiresCollectionChecks]
  343. public void NativeParallelHashSet_ForEach_Throws_Job_Iterator()
  344. {
  345. using (var container = new NativeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
  346. {
  347. var jobHandle = new ForEachIterator
  348. {
  349. Iter = container.GetEnumerator()
  350. }.Schedule();
  351. Assert.Throws<InvalidOperationException>(() => { container.Add(1); });
  352. jobHandle.Complete();
  353. }
  354. }
  355. [Test]
  356. public void NativeParallelHashSet_EIU_ExceptWith_Empty()
  357. {
  358. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  359. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  360. setA.ExceptWith(setB);
  361. ExpectedCount(ref setA, 0);
  362. setA.Dispose();
  363. setB.Dispose();
  364. }
  365. [Test]
  366. public void NativeParallelHashSet_EIU_ExceptWith_AxB()
  367. {
  368. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
  369. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
  370. setA.ExceptWith(setB);
  371. ExpectedCount(ref setA, 3);
  372. Assert.True(setA.Contains(0));
  373. Assert.True(setA.Contains(1));
  374. Assert.True(setA.Contains(2));
  375. setA.Dispose();
  376. setB.Dispose();
  377. }
  378. [Test]
  379. public void NativeParallelHashSet_EIU_ExceptWith_BxA()
  380. {
  381. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
  382. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
  383. setB.ExceptWith(setA);
  384. ExpectedCount(ref setB, 3);
  385. Assert.True(setB.Contains(6));
  386. Assert.True(setB.Contains(7));
  387. Assert.True(setB.Contains(8));
  388. setA.Dispose();
  389. setB.Dispose();
  390. }
  391. [Test]
  392. public void NativeParallelHashSet_EIU_IntersectWith_Empty()
  393. {
  394. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  395. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  396. setA.IntersectWith(setB);
  397. ExpectedCount(ref setA, 0);
  398. setA.Dispose();
  399. setB.Dispose();
  400. }
  401. [Test]
  402. public void NativeParallelHashSet_EIU_IntersectWith()
  403. {
  404. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
  405. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
  406. setA.IntersectWith(setB);
  407. ExpectedCount(ref setA, 3);
  408. Assert.True(setA.Contains(3));
  409. Assert.True(setA.Contains(4));
  410. Assert.True(setA.Contains(5));
  411. setA.Dispose();
  412. setB.Dispose();
  413. }
  414. [Test]
  415. public void NativeParallelHashSet_EIU_UnionWith_Empty()
  416. {
  417. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  418. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
  419. setA.UnionWith(setB);
  420. ExpectedCount(ref setA, 0);
  421. setA.Dispose();
  422. setB.Dispose();
  423. }
  424. [Test]
  425. public void NativeParallelHashSet_EIU_UnionWith()
  426. {
  427. var setA = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
  428. var setB = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
  429. setA.UnionWith(setB);
  430. ExpectedCount(ref setA, 9);
  431. Assert.True(setA.Contains(0));
  432. Assert.True(setA.Contains(1));
  433. Assert.True(setA.Contains(2));
  434. Assert.True(setA.Contains(3));
  435. Assert.True(setA.Contains(4));
  436. Assert.True(setA.Contains(5));
  437. Assert.True(setA.Contains(6));
  438. Assert.True(setA.Contains(7));
  439. Assert.True(setA.Contains(8));
  440. setA.Dispose();
  441. setB.Dispose();
  442. }
  443. [Test]
  444. public void NativeParallelHashSet_ToArray()
  445. {
  446. using (var set = new NativeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 })
  447. {
  448. var array = set.ToArray();
  449. Array.Sort(array);
  450. for (int i = 0, num = set.Count(); i < num; i++)
  451. {
  452. Assert.AreEqual(array[i], i);
  453. }
  454. }
  455. }
  456. [Test]
  457. public void NativeParallelHashSet_CustomAllocatorTest()
  458. {
  459. AllocatorManager.Initialize();
  460. var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
  461. ref var allocator = ref allocatorHelper.Allocator;
  462. allocator.Initialize();
  463. using (var container = new NativeParallelHashSet<int>(1, allocator.Handle))
  464. {
  465. }
  466. FastAssert.IsTrue(allocator.WasUsed);
  467. allocator.Dispose();
  468. allocatorHelper.Dispose();
  469. AllocatorManager.Shutdown();
  470. }
  471. [BurstCompile]
  472. struct BurstedCustomAllocatorJob : IJob
  473. {
  474. [NativeDisableUnsafePtrRestriction]
  475. public unsafe CustomAllocatorTests.CountingAllocator* Allocator;
  476. public void Execute()
  477. {
  478. unsafe
  479. {
  480. using (var container = new NativeParallelHashSet<int>(1, Allocator->Handle))
  481. {
  482. }
  483. }
  484. }
  485. }
  486. [Test]
  487. public unsafe void NativeParallelHashSet_BurstedCustomAllocatorTest()
  488. {
  489. AllocatorManager.Initialize();
  490. var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
  491. ref var allocator = ref allocatorHelper.Allocator;
  492. allocator.Initialize();
  493. var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator);
  494. unsafe
  495. {
  496. var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr }.Schedule();
  497. handle.Complete();
  498. }
  499. FastAssert.IsTrue(allocator.WasUsed);
  500. allocator.Dispose();
  501. allocatorHelper.Dispose();
  502. AllocatorManager.Shutdown();
  503. }
  504. }