SparseArray.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <tests/framework/Framework.h>
  6. #include <anki/util/SparseArray.h>
  7. #include <anki/util/HighRezTimer.h>
  8. #include <unordered_map>
  9. #include <ctime>
  10. ANKI_TEST(Util, SparseArray)
  11. {
  12. HeapAllocator<U8> alloc(allocAligned, nullptr);
  13. // Set same key
  14. {
  15. SparseArray<PtrSize> arr;
  16. arr.setAt(alloc, 1000, 123);
  17. auto it = arr.setAt(alloc, 1000, 124);
  18. ANKI_TEST_EXPECT_EQ(*arr.getAt(1000), 124);
  19. arr.erase(alloc, it);
  20. }
  21. // Check destroy
  22. {
  23. SparseArray<PtrSize> arr;
  24. arr.setAt(alloc, 10000, 123);
  25. arr.setAt(alloc, 20000, 124);
  26. arr.setAt(alloc, 30000, 125);
  27. ANKI_TEST_EXPECT_EQ(*arr.getAt(10000), 123);
  28. ANKI_TEST_EXPECT_EQ(*arr.getAt(20000), 124);
  29. ANKI_TEST_EXPECT_EQ(*arr.getAt(30000), 125);
  30. arr.destroy(alloc);
  31. }
  32. // Do complex insertions
  33. {
  34. SparseArray<PtrSize, U32, 32, 3> arr;
  35. arr.setAt(alloc, 32, 1);
  36. // Linear probing
  37. arr.setAt(alloc, 32 * 2, 2);
  38. arr.setAt(alloc, 32 * 3, 3);
  39. // Append to a tree
  40. arr.setAt(alloc, 32 * 4, 4);
  41. // Linear probing
  42. arr.setAt(alloc, 32 + 1, 5);
  43. // Evict node
  44. arr.setAt(alloc, 32 * 2 + 1, 5);
  45. ANKI_TEST_EXPECT_EQ(arr.getSize(), 6);
  46. arr.destroy(alloc);
  47. }
  48. // Fuzzy test
  49. {
  50. const U MAX = 1000;
  51. SparseArray<int, U32, 32, 3> arr;
  52. std::vector<int> numbers;
  53. srand(time(nullptr));
  54. // Insert random
  55. for(U i = 0; i < MAX; ++i)
  56. {
  57. U num;
  58. while(1)
  59. {
  60. num = rand();
  61. if(std::find(numbers.begin(), numbers.end(), int(num)) == numbers.end())
  62. {
  63. // Not found
  64. ANKI_TEST_EXPECT_EQ(arr.getAt(num), arr.getEnd());
  65. arr.setAt(alloc, num, num);
  66. numbers.push_back(num);
  67. break;
  68. }
  69. else
  70. {
  71. // Found
  72. ANKI_TEST_EXPECT_NEQ(arr.getAt(num), arr.getEnd());
  73. }
  74. }
  75. }
  76. ANKI_TEST_EXPECT_EQ(arr.getSize(), MAX);
  77. // Remove randomly
  78. U count = MAX;
  79. while(count--)
  80. {
  81. U idx = rand() % (count + 1);
  82. int num = numbers[idx];
  83. numbers.erase(numbers.begin() + idx);
  84. auto it = arr.getAt(num);
  85. ANKI_TEST_EXPECT_NEQ(it, arr.getEnd());
  86. ANKI_TEST_EXPECT_EQ(*it, num);
  87. arr.erase(alloc, it);
  88. }
  89. }
  90. // Fuzzy test #2: Do random insertions and removals
  91. {
  92. const U MAX = 10000;
  93. SparseArray<int, U32, 64, 4> arr;
  94. using StlMap =
  95. std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, HeapAllocator<std::pair<int, int>>>;
  96. StlMap map(10, std::hash<int>(), std::equal_to<int>(), alloc);
  97. for(U i = 0; i < MAX; ++i)
  98. {
  99. const Bool insert = (rand() & 1) || arr.getSize() == 0;
  100. if(insert)
  101. {
  102. const I idx = rand();
  103. if(map.find(idx) != map.end())
  104. {
  105. continue;
  106. }
  107. arr.setAt(alloc, idx, idx);
  108. map[idx] = idx;
  109. arr.validate();
  110. }
  111. else
  112. {
  113. const U idx = U(rand()) % map.size();
  114. auto it = std::next(std::begin(map), idx);
  115. auto it2 = arr.getAt(it->second);
  116. ANKI_TEST_EXPECT_NEQ(it2, arr.getEnd());
  117. map.erase(it);
  118. arr.erase(alloc, it2);
  119. arr.validate();
  120. }
  121. // Iterate and check
  122. {
  123. StlMap bMap = map;
  124. auto it = arr.getBegin();
  125. while(it != arr.getEnd())
  126. {
  127. I val = *it;
  128. auto it2 = bMap.find(val);
  129. ANKI_TEST_EXPECT_NEQ(it2, bMap.end());
  130. bMap.erase(it2);
  131. ++it;
  132. }
  133. }
  134. }
  135. arr.destroy(alloc);
  136. }
  137. }
  138. static PtrSize akAllocSize = 0;
  139. static ANKI_DONT_INLINE void* allocAlignedAk(void* userData, void* ptr, PtrSize size, PtrSize alignment)
  140. {
  141. if(ptr == nullptr)
  142. {
  143. akAllocSize += size;
  144. }
  145. return allocAligned(userData, ptr, size, alignment);
  146. }
  147. static PtrSize stlAllocSize = 0;
  148. static ANKI_DONT_INLINE void* allocAlignedStl(void* userData, void* ptr, PtrSize size, PtrSize alignment)
  149. {
  150. if(ptr == nullptr)
  151. {
  152. stlAllocSize += size;
  153. }
  154. return allocAligned(userData, ptr, size, alignment);
  155. }
  156. ANKI_TEST(Util, SparseArrayBench)
  157. {
  158. HeapAllocator<U8> allocAk(allocAlignedAk, nullptr);
  159. HeapAllocator<U8> allocStl(allocAlignedStl, nullptr);
  160. HeapAllocator<U8> allocTml(allocAligned, nullptr);
  161. using StlMap = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, HeapAllocator<std::pair<int, int>>>;
  162. StlMap stdMap(10, std::hash<int>(), std::equal_to<int>(), allocStl);
  163. using AkMap = SparseArray<int, U32, 1024, 16>;
  164. AkMap akMap;
  165. HighRezTimer timer;
  166. const U COUNT = 1024 * 1024 * 5;
  167. // Create a huge set
  168. DynamicArrayAuto<int> vals(allocTml);
  169. {
  170. std::unordered_map<int, int> tmpMap;
  171. vals.create(COUNT);
  172. for(U i = 0; i < COUNT; ++i)
  173. {
  174. // Put unique keys
  175. int v;
  176. do
  177. {
  178. v = rand();
  179. } while(tmpMap.find(v) != tmpMap.end());
  180. tmpMap[v] = 1;
  181. vals[i] = v;
  182. }
  183. }
  184. // Insertion
  185. {
  186. // AnkI
  187. timer.start();
  188. for(U i = 0; i < COUNT; ++i)
  189. {
  190. akMap.setAt(allocAk, vals[i], vals[i]);
  191. }
  192. timer.stop();
  193. HighRezTimer::Scalar akTime = timer.getElapsedTime();
  194. // STL
  195. timer.start();
  196. for(U i = 0; i < COUNT; ++i)
  197. {
  198. stdMap[vals[i]] = vals[i];
  199. }
  200. timer.stop();
  201. HighRezTimer::Scalar stlTime = timer.getElapsedTime();
  202. ANKI_TEST_LOGI("Inserting bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0);
  203. }
  204. // Search
  205. {
  206. int count = 0;
  207. // Find values AnKi
  208. timer.start();
  209. for(U i = 0; i < COUNT; ++i)
  210. {
  211. auto it = akMap.getAt(vals[i]);
  212. count += *it;
  213. }
  214. timer.stop();
  215. HighRezTimer::Scalar akTime = timer.getElapsedTime();
  216. // Find values STL
  217. timer.start();
  218. for(U i = 0; i < COUNT; ++i)
  219. {
  220. count += stdMap[vals[i]];
  221. }
  222. timer.stop();
  223. HighRezTimer::Scalar stlTime = timer.getElapsedTime();
  224. ANKI_TEST_LOGI("Find bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0);
  225. }
  226. // Mem usage
  227. const PtrSize stlMemUsage = stlAllocSize + sizeof(stdMap);
  228. const PtrSize akMemUsage = akAllocSize + sizeof(akMap);
  229. ANKI_TEST_LOGI(
  230. "Mem usage: STL %llu AnKi %llu | %f%%", stlMemUsage, akMemUsage, F64(stlMemUsage) / akMemUsage * 100.0);
  231. // Deletes
  232. {
  233. const U DEL_COUNT = COUNT / 2;
  234. DynamicArrayAuto<AkMap::Iterator> delValsAk(allocTml);
  235. delValsAk.create(DEL_COUNT);
  236. DynamicArrayAuto<StlMap::iterator> delValsStl(allocTml);
  237. delValsStl.create(DEL_COUNT);
  238. std::unordered_map<int, int> tmpMap;
  239. for(U i = 0; i < DEL_COUNT; ++i)
  240. {
  241. // Put unique keys
  242. int v;
  243. do
  244. {
  245. v = vals[rand() % COUNT];
  246. } while(tmpMap.find(v) != tmpMap.end());
  247. tmpMap[v] = 1;
  248. delValsAk[i] = akMap.getAt(v);
  249. delValsStl[i] = stdMap.find(v);
  250. }
  251. // Random delete AnKi
  252. timer.start();
  253. for(U i = 0; i < DEL_COUNT; ++i)
  254. {
  255. akMap.erase(allocAk, delValsAk[i]);
  256. }
  257. timer.stop();
  258. HighRezTimer::Scalar akTime = timer.getElapsedTime();
  259. // Random delete STL
  260. timer.start();
  261. for(U i = 0; i < DEL_COUNT; ++i)
  262. {
  263. stdMap.erase(delValsStl[i]);
  264. }
  265. timer.stop();
  266. HighRezTimer::Scalar stlTime = timer.getElapsedTime();
  267. ANKI_TEST_LOGI("Deleting bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0);
  268. }
  269. akMap.destroy(allocAk);
  270. }