SparseArray.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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. #include <algorithm>
  11. #include <malloc.h>
  12. namespace anki
  13. {
  14. namespace
  15. {
  16. static I64 constructor0Count = 0;
  17. static I64 constructor1Count = 0;
  18. static I64 constructor2Count = 0;
  19. static I64 constructor3Count = 0;
  20. static I64 destructorCount = 0;
  21. static I64 copyCount = 0;
  22. static I64 moveCount = 0;
  23. class SAFoo
  24. {
  25. public:
  26. int m_x;
  27. SAFoo()
  28. : m_x(0)
  29. {
  30. ++constructor0Count;
  31. }
  32. SAFoo(int x)
  33. : m_x(x)
  34. {
  35. ++constructor1Count;
  36. }
  37. SAFoo(const SAFoo& b)
  38. : m_x(b.m_x)
  39. {
  40. ++constructor2Count;
  41. }
  42. SAFoo(SAFoo&& b)
  43. : m_x(b.m_x)
  44. {
  45. b.m_x = 0;
  46. ++constructor3Count;
  47. }
  48. ~SAFoo()
  49. {
  50. ++destructorCount;
  51. }
  52. SAFoo& operator=(const SAFoo& b)
  53. {
  54. m_x = b.m_x;
  55. ++copyCount;
  56. return *this;
  57. }
  58. SAFoo& operator=(SAFoo&& b)
  59. {
  60. m_x = b.m_x;
  61. b.m_x = 0;
  62. ++moveCount;
  63. return *this;
  64. }
  65. static void checkCalls()
  66. {
  67. ANKI_TEST_EXPECT_EQ(constructor0Count, 0); // default
  68. ANKI_TEST_EXPECT_GT(constructor1Count, 0);
  69. ANKI_TEST_EXPECT_EQ(constructor2Count, 0); // copy
  70. ANKI_TEST_EXPECT_GT(constructor3Count, 0); // move
  71. ANKI_TEST_EXPECT_EQ(destructorCount, constructor1Count + constructor3Count);
  72. ANKI_TEST_EXPECT_EQ(copyCount, 0); // copy
  73. ANKI_TEST_EXPECT_GEQ(moveCount, 0); // move
  74. constructor0Count = constructor1Count = constructor2Count = constructor3Count = destructorCount = copyCount =
  75. moveCount = 0;
  76. }
  77. };
  78. }
  79. }
  80. ANKI_TEST(Util, SparseArray)
  81. {
  82. HeapAllocator<U8> alloc(allocAligned, nullptr);
  83. // Set same key
  84. {
  85. SparseArray<PtrSize> arr;
  86. arr.emplace(alloc, 1000, 123);
  87. arr.emplace(alloc, 1000, 124);
  88. auto it = arr.find(1000);
  89. ANKI_TEST_EXPECT_EQ(*it, 124);
  90. arr.erase(alloc, it);
  91. }
  92. // Check destroy and grow
  93. {
  94. SparseArray<SAFoo> arr(64, 2);
  95. arr.emplace(alloc, 64 * 1, 123);
  96. arr.emplace(alloc, 64 * 2, 124);
  97. arr.emplace(alloc, 64 * 3, 125);
  98. ANKI_TEST_EXPECT_EQ(arr.find(64 * 1)->m_x, 123);
  99. ANKI_TEST_EXPECT_EQ(arr.find(64 * 2)->m_x, 124);
  100. ANKI_TEST_EXPECT_EQ(arr.find(64 * 3)->m_x, 125);
  101. arr.destroy(alloc);
  102. SAFoo::checkCalls();
  103. }
  104. // Do complex insertions
  105. {
  106. SparseArray<SAFoo, U32> arr(64, 3);
  107. arr.emplace(alloc, 64 * 0 - 1, 1);
  108. // Linear probing to 0
  109. arr.emplace(alloc, 64 * 1 - 1, 2);
  110. // Linear probing to 1
  111. arr.emplace(alloc, 64 * 2 - 1, 3);
  112. // Linear probing to 2
  113. arr.emplace(alloc, 1, 3);
  114. // Swap
  115. arr.emplace(alloc, 64 * 1, 3);
  116. ANKI_TEST_EXPECT_EQ(arr.getSize(), 5);
  117. arr.destroy(alloc);
  118. SAFoo::checkCalls();
  119. }
  120. // Fuzzy test
  121. {
  122. const U MAX = 10000;
  123. SparseArray<SAFoo, U32> arr;
  124. std::vector<int> numbers;
  125. srand(time(nullptr));
  126. // Insert random
  127. for(U i = 0; i < MAX; ++i)
  128. {
  129. U num;
  130. while(1)
  131. {
  132. num = rand();
  133. if(std::find(numbers.begin(), numbers.end(), int(num)) == numbers.end())
  134. {
  135. // Not found
  136. ANKI_TEST_EXPECT_EQ(arr.find(num), arr.getEnd());
  137. arr.emplace(alloc, num, num);
  138. ANKI_TEST_EXPECT_EQ(arr.getSize(), i + 1);
  139. numbers.push_back(num);
  140. break;
  141. }
  142. else
  143. {
  144. // Found
  145. ANKI_TEST_EXPECT_NEQ(arr.find(num), arr.getEnd());
  146. }
  147. }
  148. arr.validate();
  149. }
  150. ANKI_TEST_EXPECT_EQ(arr.getSize(), MAX);
  151. // Remove randomly
  152. U count = MAX;
  153. while(count--)
  154. {
  155. U idx = rand() % (count + 1);
  156. int num = numbers[idx];
  157. numbers.erase(numbers.begin() + idx);
  158. auto it = arr.find(num);
  159. ANKI_TEST_EXPECT_NEQ(it, arr.getEnd());
  160. ANKI_TEST_EXPECT_EQ(it->m_x, num);
  161. arr.erase(alloc, it);
  162. arr.validate();
  163. }
  164. }
  165. // Fuzzy test #2: Do random insertions and removals
  166. {
  167. const U MAX = 10000;
  168. SparseArray<SAFoo, U32> arr;
  169. using StlMap =
  170. std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, HeapAllocator<std::pair<int, int>>>;
  171. StlMap map(10, std::hash<int>(), std::equal_to<int>(), alloc);
  172. for(U i = 0; i < MAX; ++i)
  173. {
  174. const Bool insert = (rand() & 1) || arr.getSize() == 0;
  175. if(insert)
  176. {
  177. const I idx = rand();
  178. if(map.find(idx) != map.end())
  179. {
  180. continue;
  181. }
  182. arr.emplace(alloc, idx, idx + 1);
  183. map[idx] = idx + 1;
  184. arr.validate();
  185. }
  186. else
  187. {
  188. const U idx = U(rand()) % map.size();
  189. auto it = std::next(std::begin(map), idx);
  190. auto it2 = arr.find(it->first);
  191. ANKI_TEST_EXPECT_NEQ(it2, arr.getEnd());
  192. ANKI_TEST_EXPECT_EQ(it->second, it2->m_x);
  193. map.erase(it);
  194. arr.erase(alloc, it2);
  195. arr.validate();
  196. }
  197. // Iterate and check
  198. {
  199. StlMap bMap = map;
  200. auto it = arr.getBegin();
  201. while(it != arr.getEnd())
  202. {
  203. I key = it->m_x - 1;
  204. auto it2 = bMap.find(key);
  205. ANKI_TEST_EXPECT_NEQ(it2, bMap.end());
  206. bMap.erase(it2);
  207. ++it;
  208. }
  209. }
  210. }
  211. arr.destroy(alloc);
  212. // Check what the SparseArray have called
  213. SAFoo::checkCalls();
  214. }
  215. }
  216. static I64 akAllocSize = 0;
  217. static I64 akMaxAllocSize = 0;
  218. static ANKI_DONT_INLINE void* allocAlignedAk(void* userData, void* ptr, PtrSize size, PtrSize alignment)
  219. {
  220. if(ptr == nullptr)
  221. {
  222. #if ANKI_OS == ANKI_OS_LINUX
  223. akAllocSize += size;
  224. akMaxAllocSize = max(akMaxAllocSize, akAllocSize);
  225. #endif
  226. return malloc(size);
  227. }
  228. else
  229. {
  230. #if ANKI_OS == ANKI_OS_LINUX
  231. PtrSize s = malloc_usable_size(ptr);
  232. akAllocSize -= s;
  233. #endif
  234. free(ptr);
  235. return nullptr;
  236. }
  237. }
  238. static I64 stlAllocSize = 0;
  239. static I64 stlMaxAllocSize = 0;
  240. static ANKI_DONT_INLINE void* allocAlignedStl(void* userData, void* ptr, PtrSize size, PtrSize alignment)
  241. {
  242. if(ptr == nullptr)
  243. {
  244. #if ANKI_OS == ANKI_OS_LINUX
  245. stlAllocSize += size;
  246. stlMaxAllocSize = max(stlMaxAllocSize, stlAllocSize);
  247. #endif
  248. return malloc(size);
  249. }
  250. else
  251. {
  252. #if ANKI_OS == ANKI_OS_LINUX
  253. PtrSize s = malloc_usable_size(ptr);
  254. stlAllocSize -= s;
  255. #endif
  256. free(ptr);
  257. return nullptr;
  258. }
  259. }
  260. ANKI_TEST(Util, SparseArrayBench)
  261. {
  262. HeapAllocator<U8> allocAk(allocAlignedAk, nullptr);
  263. HeapAllocator<U8> allocStl(allocAlignedStl, nullptr);
  264. HeapAllocator<U8> allocTml(allocAligned, nullptr);
  265. using StlMap = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, HeapAllocator<std::pair<int, int>>>;
  266. StlMap stdMap(10, std::hash<int>(), std::equal_to<int>(), allocStl);
  267. using AkMap = SparseArray<int, U32>;
  268. AkMap akMap(256, log2(256), 0.90f);
  269. HighRezTimer timer;
  270. const U COUNT = 1024 * 1024 * 6;
  271. // Create a huge set
  272. std::vector<int> vals;
  273. {
  274. std::unordered_map<int, int> tmpMap;
  275. for(U i = 0; i < COUNT; ++i)
  276. {
  277. // Put unique keys
  278. int v;
  279. do
  280. {
  281. v = rand();
  282. } while(tmpMap.find(v) != tmpMap.end() && v != 0);
  283. tmpMap[v] = 1;
  284. vals.push_back(v);
  285. }
  286. }
  287. // Insertion
  288. {
  289. // AnkI
  290. timer.start();
  291. for(U i = 0; i < COUNT; ++i)
  292. {
  293. akMap.emplace(allocAk, vals[i], vals[i]);
  294. }
  295. timer.stop();
  296. HighRezTimer::Scalar akTime = timer.getElapsedTime();
  297. // STL
  298. timer.start();
  299. for(U i = 0; i < COUNT; ++i)
  300. {
  301. stdMap[vals[i]] = vals[i];
  302. }
  303. timer.stop();
  304. HighRezTimer::Scalar stlTime = timer.getElapsedTime();
  305. ANKI_TEST_LOGI("Inserting bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0);
  306. }
  307. // Search
  308. {
  309. // Search in random order
  310. std::random_shuffle(vals.begin(), vals.end());
  311. int count = 0;
  312. // Find values AnKi
  313. timer.start();
  314. for(U i = 0; i < COUNT; ++i)
  315. {
  316. auto it = akMap.find(vals[i]);
  317. count += *it;
  318. }
  319. timer.stop();
  320. HighRezTimer::Scalar akTime = timer.getElapsedTime();
  321. // Find values STL
  322. timer.start();
  323. for(U i = 0; i < COUNT; ++i)
  324. {
  325. count += stdMap[vals[i]];
  326. }
  327. timer.stop();
  328. HighRezTimer::Scalar stlTime = timer.getElapsedTime();
  329. // Print the "count" so that the compiler won't optimize it
  330. ANKI_TEST_LOGI("Find bench: STL %f AnKi %f | %f%% (r:%d)", stlTime, akTime, stlTime / akTime * 100.0, count);
  331. }
  332. // Mem usage
  333. const I64 stlMemUsage = stlMaxAllocSize + sizeof(stdMap);
  334. const I64 akMemUsage = akMaxAllocSize + sizeof(akMap);
  335. ANKI_TEST_LOGI("Max mem usage: STL %lli AnKi %lli | %f%% (At any given time what was the max mem usage)",
  336. stlMemUsage,
  337. akMemUsage,
  338. F64(stlMemUsage) / akMemUsage * 100.0);
  339. // Deletes
  340. {
  341. // Remove in random order
  342. std::random_shuffle(vals.begin(), vals.end());
  343. // Random delete AnKi
  344. HighRezTimer::Scalar akTime = 0.0;
  345. for(U i = 0; i < vals.size(); ++i)
  346. {
  347. auto it = akMap.find(vals[i]);
  348. timer.start();
  349. akMap.erase(allocAk, it);
  350. timer.stop();
  351. akTime += timer.getElapsedTime();
  352. }
  353. // Random delete STL
  354. HighRezTimer::Scalar stlTime = 0.0;
  355. for(U i = 0; i < vals.size(); ++i)
  356. {
  357. auto it = stdMap.find(vals[i]);
  358. timer.start();
  359. stdMap.erase(it);
  360. timer.stop();
  361. stlTime += timer.getElapsedTime();
  362. }
  363. ANKI_TEST_LOGI("Deleting bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0);
  364. }
  365. akMap.destroy(allocAk);
  366. }