// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include ANKI_TEST(Util, SparseArray) { HeapAllocator alloc(allocAligned, nullptr); // Set same key { SparseArray arr; arr.setAt(alloc, 1000, 123); auto it = arr.setAt(alloc, 1000, 124); ANKI_TEST_EXPECT_EQ(*arr.getAt(1000), 124); arr.erase(alloc, it); } // Check destroy { SparseArray arr; arr.setAt(alloc, 10000, 123); arr.setAt(alloc, 20000, 124); arr.setAt(alloc, 30000, 125); ANKI_TEST_EXPECT_EQ(*arr.getAt(10000), 123); ANKI_TEST_EXPECT_EQ(*arr.getAt(20000), 124); ANKI_TEST_EXPECT_EQ(*arr.getAt(30000), 125); arr.destroy(alloc); } // Do complex insertions { SparseArray arr; arr.setAt(alloc, 32, 1); // Linear probing arr.setAt(alloc, 32 * 2, 2); arr.setAt(alloc, 32 * 3, 3); // Append to a tree arr.setAt(alloc, 32 * 4, 4); // Linear probing arr.setAt(alloc, 32 + 1, 5); // Evict node arr.setAt(alloc, 32 * 2 + 1, 5); ANKI_TEST_EXPECT_EQ(arr.getSize(), 6); arr.destroy(alloc); } // Fuzzy test { const U MAX = 1000; SparseArray arr; std::vector numbers; srand(time(nullptr)); // Insert random for(U i = 0; i < MAX; ++i) { U num; while(1) { num = rand(); if(std::find(numbers.begin(), numbers.end(), int(num)) == numbers.end()) { // Not found ANKI_TEST_EXPECT_EQ(arr.getAt(num), arr.getEnd()); arr.setAt(alloc, num, num); numbers.push_back(num); break; } else { // Found ANKI_TEST_EXPECT_NEQ(arr.getAt(num), arr.getEnd()); } } } ANKI_TEST_EXPECT_EQ(arr.getSize(), MAX); // Remove randomly U count = MAX; while(count--) { U idx = rand() % (count + 1); int num = numbers[idx]; numbers.erase(numbers.begin() + idx); auto it = arr.getAt(num); ANKI_TEST_EXPECT_NEQ(it, arr.getEnd()); ANKI_TEST_EXPECT_EQ(*it, num); arr.erase(alloc, it); } } // Fuzzy test #2: Do random insertions and removals { const U MAX = 10000; SparseArray arr; using StlMap = std::unordered_map, std::equal_to, HeapAllocator>>; StlMap map(10, std::hash(), std::equal_to(), alloc); for(U i = 0; i < MAX; ++i) { const Bool insert = (rand() & 1) || arr.getSize() == 0; if(insert) { const I idx = rand(); if(map.find(idx) != map.end()) { continue; } arr.setAt(alloc, idx, idx); map[idx] = idx; arr.validate(); } else { const U idx = U(rand()) % map.size(); auto it = std::next(std::begin(map), idx); auto it2 = arr.getAt(it->second); ANKI_TEST_EXPECT_NEQ(it2, arr.getEnd()); map.erase(it); arr.erase(alloc, it2); arr.validate(); } // Iterate and check { StlMap bMap = map; auto it = arr.getBegin(); while(it != arr.getEnd()) { I val = *it; auto it2 = bMap.find(val); ANKI_TEST_EXPECT_NEQ(it2, bMap.end()); bMap.erase(it2); ++it; } } } arr.destroy(alloc); } } static ANKI_DONT_INLINE void* allocAlignedAk(void* userData, void* ptr, PtrSize size, PtrSize alignment) { return allocAligned(userData, ptr, size, alignment); } static ANKI_DONT_INLINE void* allocAlignedStl(void* userData, void* ptr, PtrSize size, PtrSize alignment) { return allocAligned(userData, ptr, size, alignment); } ANKI_TEST(Util, SparseArrayBench) { HeapAllocator allocAk(allocAlignedAk, nullptr); HeapAllocator allocStl(allocAlignedStl, nullptr); HeapAllocator allocTml(allocAligned, nullptr); using StlMap = std::unordered_map, std::equal_to, HeapAllocator>>; StlMap stdMap(10, std::hash(), std::equal_to(), allocStl); using AkMap = SparseArray; AkMap akMap; HighRezTimer timer; const U COUNT = 1024 * 1024 * 5; // Create a huge set DynamicArrayAuto vals(allocTml); { std::unordered_map tmpMap; vals.create(COUNT); for(U i = 0; i < COUNT; ++i) { // Put unique keys int v; do { v = rand(); } while(tmpMap.find(v) != tmpMap.end()); tmpMap[v] = 1; vals[i] = v; } } // Insertion { // AnkI timer.start(); for(U i = 0; i < COUNT; ++i) { akMap.setAt(allocAk, vals[i], vals[i]); } timer.stop(); HighRezTimer::Scalar akTime = timer.getElapsedTime(); // STL timer.start(); for(U i = 0; i < COUNT; ++i) { stdMap[vals[i]] = vals[i]; } timer.stop(); HighRezTimer::Scalar stlTime = timer.getElapsedTime(); ANKI_TEST_LOGI("Inserting bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0); } // Search #if 1 { int count = 0; // Find values AnKi timer.start(); for(U i = 0; i < COUNT; ++i) { auto it = akMap.getAt(vals[i]); count += *it; } timer.stop(); HighRezTimer::Scalar akTime = timer.getElapsedTime(); // Find values STL timer.start(); for(U i = 0; i < COUNT; ++i) { count += stdMap[vals[i]]; } timer.stop(); HighRezTimer::Scalar stlTime = timer.getElapsedTime(); ANKI_TEST_LOGI("Find bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0); } #endif // Deletes { const U DEL_COUNT = COUNT / 2; DynamicArrayAuto delValsAk(allocTml); delValsAk.create(DEL_COUNT); DynamicArrayAuto delValsStl(allocTml); delValsStl.create(DEL_COUNT); std::unordered_map tmpMap; for(U i = 0; i < DEL_COUNT; ++i) { // Put unique keys int v; do { v = vals[rand() % COUNT]; } while(tmpMap.find(v) != tmpMap.end()); tmpMap[v] = 1; delValsAk[i] = akMap.getAt(v); delValsStl[i] = stdMap.find(v); } // Random delete AnKi timer.start(); for(U i = 0; i < DEL_COUNT; ++i) { akMap.erase(allocAk, delValsAk[i]); } timer.stop(); HighRezTimer::Scalar akTime = timer.getElapsedTime(); // Random delete STL timer.start(); for(U i = 0; i < DEL_COUNT; ++i) { stdMap.erase(delValsStl[i]); } timer.stop(); HighRezTimer::Scalar stlTime = timer.getElapsedTime(); ANKI_TEST_LOGI("Deleting bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0); } akMap.destroy(allocAk); }