///////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ///////////////////////////////////////////////////////////////////////////// #include "EASTLBenchmark.h" #include "EASTLTest.h" #include #include #include #include #ifdef _MSC_VER #pragma warning(push, 0) #pragma warning(disable: 4350) #endif #include #include #include #include #ifdef _MSC_VER #pragma warning(pop) #endif using namespace EA; typedef std::vector StdVectorUint64; typedef eastl::tuple_vector EaTupleVectorUint64; struct PaddingStruct { char padding[56] = { 0 }; }; static const PaddingStruct DefaultPadding; typedef eastl::tuple PaddedTuple; typedef std::vector StdVectorUint64Padded; typedef eastl::tuple_vector EaTupleVectorUint64Padded; namespace { ////////////////////////////////////////////////////////////////////////////// // MovableType // struct MovableType { int8_t* mpData; enum { kDataSize = 128 }; MovableType() : mpData(new int8_t[kDataSize]) { memset(mpData, 0, kDataSize); } MovableType(const MovableType& x) : mpData(new int8_t[kDataSize]) { memcpy(mpData, x.mpData, kDataSize); } MovableType& operator=(const MovableType& x) { if(!mpData) mpData = new int8_t[kDataSize]; memcpy(mpData, x.mpData, kDataSize); return *this; } #if EASTL_MOVE_SEMANTICS_ENABLED MovableType(MovableType&& x) EA_NOEXCEPT : mpData(x.mpData) { x.mpData = NULL; } MovableType& operator=(MovableType&& x) { eastl::swap(mpData, x.mpData); // In practice it may not be right to do a swap, depending on the case. return *this; } #endif ~MovableType() { delete[] mpData; } }; ////////////////////////////////////////////////////////////////////////////// // AutoRefCount // // Basic ref-counted object. // template class AutoRefCount { public: T* mpObject; public: AutoRefCount() EA_NOEXCEPT : mpObject(NULL) {} AutoRefCount(T* pObject) EA_NOEXCEPT : mpObject(pObject) { if(mpObject) mpObject->AddRef(); } AutoRefCount(T* pObject, int) EA_NOEXCEPT : mpObject(pObject) { // Inherit the existing refcount. } AutoRefCount(const AutoRefCount& x) EA_NOEXCEPT : mpObject(x.mpObject) { if(mpObject) mpObject->AddRef(); } AutoRefCount& operator=(const AutoRefCount& x) { return operator=(x.mpObject); } AutoRefCount& operator=(T* pObject) { if(pObject != mpObject) { T* const pTemp = mpObject; // Create temporary to prevent possible problems with re-entrancy. if(pObject) pObject->AddRef(); mpObject = pObject; if(pTemp) pTemp->Release(); } return *this; } #if EASTL_MOVE_SEMANTICS_ENABLED AutoRefCount(AutoRefCount&& x) EA_NOEXCEPT : mpObject(x.mpObject) { x.mpObject = NULL; } AutoRefCount& operator=(AutoRefCount&& x) { if(mpObject) mpObject->Release(); mpObject = x.mpObject; x.mpObject = NULL; return *this; } #endif ~AutoRefCount() { if(mpObject) mpObject->Release(); } T& operator *() const EA_NOEXCEPT { return *mpObject; } T* operator ->() const EA_NOEXCEPT { return mpObject; } operator T*() const EA_NOEXCEPT { return mpObject; } }; // class AutoRefCount struct RefCounted { int mRefCount; static int msAddRefCount; static int msReleaseCount; RefCounted() : mRefCount(1) {} int AddRef() { ++msAddRefCount; return ++mRefCount; } int Release() { ++msReleaseCount; if(mRefCount > 1) return --mRefCount; delete this; return 0; } }; int RefCounted::msAddRefCount = 0; int RefCounted::msReleaseCount = 0; } // namespace namespace { template void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& intVector) { stopwatch.Restart(); for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) c.push_back((uint64_t)intVector[j]); stopwatch.Stop(); } template void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c) { uint64_t temp = 0; stopwatch.Restart(); for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += c[j]; stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); } void TestBracket(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) { uint64_t temp = 0; stopwatch.Restart(); for (typename EaTupleVectorUint64::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += eastl::get<0>(c[j]); stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); } template void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c) { stopwatch.Restart(); typedef typename Container::iterator iterator_t; // This typedef is required to get this code to compile on RVCT iterator_t it = eastl::find(c.begin(), c.end(), UINT64_C(0xffffffffffff)); stopwatch.Stop(); if(it != c.end()) sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it); } void TestFind(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) { eastl::tuple val(0xffffffffffff); stopwatch.Restart(); EaTupleVectorUint64::iterator it = eastl::find(c.begin(), c.end(), val); stopwatch.Stop(); if (it != c.end()) sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it)); } template void TestSort(EA::StdC::Stopwatch& stopwatch, Container& c) { // Intentionally use eastl sort in order to measure just // vector access speed and not be polluted by sort speed. stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end()); stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(c[0] & 0xffffffff)); } void TestSort(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c) { // Intentionally use eastl sort in order to measure just // vector access speed and not be polluted by sort speed. stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end()); stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); } template void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c) { typename Container::size_type j, jEnd; typename Container::iterator it; stopwatch.Restart(); for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j) { it = c.insert(it, UINT64_C(0xffffffffffff)); if(it == c.end()) // Try to safely increment the iterator three times. it = c.begin(); if(++it == c.end()) it = c.begin(); if(++it == c.end()) it = c.begin(); } stopwatch.Stop(); } template void TestErase(EA::StdC::Stopwatch& stopwatch, Container& c) { typename Container::size_type j, jEnd; typename Container::iterator it; stopwatch.Restart(); for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j) { it = c.erase(it); if(it == c.end()) // Try to safely increment the iterator three times. it = c.begin(); if(++it == c.end()) it = c.begin(); if(++it == c.end()) it = c.begin(); } stopwatch.Stop(); } template void TestMoveReallocate(EA::StdC::Stopwatch& stopwatch, Container& c) { stopwatch.Restart(); while(c.size() < 8192) c.resize(c.capacity() + 1); stopwatch.Stop(); } template void TestMoveErase(EA::StdC::Stopwatch& stopwatch, Container& c) { stopwatch.Restart(); while(!c.empty()) c.erase(c.begin()); stopwatch.Stop(); } ////////////////////////////////////////////////////////////////////////// // Variations of test functions for the Padded structures template void TestTuplePushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& intVector) { stopwatch.Restart(); for (eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) { PaddedTuple tup((uint64_t)intVector[j], DefaultPadding); c.push_back(tup); } stopwatch.Stop(); } template void TestTupleBracket(EA::StdC::Stopwatch& stopwatch, Container& c) { uint64_t temp = 0; stopwatch.Restart(); for (typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++) temp += eastl::get<0>(c[j]); stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff)); } template void TestTupleFind(EA::StdC::Stopwatch& stopwatch, Container& c) { stopwatch.Restart(); typedef typename Container::iterator iterator_t; // This typedef is required to get this code to compile on RVCT iterator_t it = eastl::find_if(c.begin(), c.end(), [](auto tup) { return eastl::get<0>(tup) == 0xFFFFFFFF; }); stopwatch.Stop(); if (it != c.end()) sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it)); } template void TestTupleSort(EA::StdC::Stopwatch& stopwatch, Container& c) { // Intentionally use eastl sort in order to measure just // vector access speed and not be polluted by sort speed. stopwatch.Restart(); eastl::quick_sort(c.begin(), c.end(), [](auto a, auto b) { return eastl::get<0>(a) < eastl::get<0>(b); }); stopwatch.Stop(); sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff)); } template void TestTupleInsert(EA::StdC::Stopwatch& stopwatch, Container& c) { typename Container::size_type j, jEnd; typename Container::iterator it; PaddedTuple tup(0xFFFFFFFF, DefaultPadding); stopwatch.Restart(); for (j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j) { it = c.insert(it, tup); if (it == c.end()) // Try to safely increment the iterator three times. it = c.begin(); if (++it == c.end()) it = c.begin(); if (++it == c.end()) it = c.begin(); } stopwatch.Stop(); } template void TestTupleErase(EA::StdC::Stopwatch& stopwatch, Container& c) { typename Container::size_type j, jEnd; typename Container::iterator it; stopwatch.Restart(); for (j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j) { it = c.erase(it); if (it == c.end()) // Try to safely increment the iterator three times. it = c.begin(); if (++it == c.end()) it = c.begin(); if (++it == c.end()) it = c.begin(); } stopwatch.Stop(); } } // namespace void BenchmarkTupleVector() { EASTLTest_Printf("TupleVector\n"); EA::UnitTest::RandGenT rng(EA::UnitTest::GetRandSeed()); EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles); EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles); { eastl::vector intVector(100000); eastl::generate(intVector.begin(), intVector.end(), rng); for(int i = 0; i < 2; i++) { StdVectorUint64 stdVectorUint64; EaTupleVectorUint64 eaTupleVectorUint64; /////////////////////////////// // Test push_back /////////////////////////////// TestPushBack(stopwatch1, stdVectorUint64, intVector); TestPushBack(stopwatch2, eaTupleVectorUint64, intVector); if(i == 1) Benchmark::AddResult("tuple_vector/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test operator[]. /////////////////////////////// TestBracket(stopwatch1, stdVectorUint64); TestBracket(stopwatch2, eaTupleVectorUint64); if(i == 1) Benchmark::AddResult("tuple_vector/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test iteration via find(). /////////////////////////////// TestFind(stopwatch1, stdVectorUint64); TestFind(stopwatch2, eaTupleVectorUint64); TestFind(stopwatch1, stdVectorUint64); TestFind(stopwatch2, eaTupleVectorUint64); if(i == 1) Benchmark::AddResult("tuple_vector/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test sort /////////////////////////////// // Currently VC++ complains about our sort function decrementing std::iterator that is already at begin(). In the strictest sense, // that's a valid complaint, but we aren't testing std STL here. We will want to revise our sort function eventually. #if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2) TestSort(stopwatch1, stdVectorUint64); TestSort(stopwatch2, eaTupleVectorUint64); if(i == 1) Benchmark::AddResult("tuple_vector/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); #endif /////////////////////////////// // Test insert /////////////////////////////// TestInsert(stopwatch1, stdVectorUint64); TestInsert(stopwatch2, eaTupleVectorUint64); if(i == 1) Benchmark::AddResult("tuple_vector/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test erase /////////////////////////////// TestErase(stopwatch1, stdVectorUint64); TestErase(stopwatch2, eaTupleVectorUint64); if(i == 1) Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////////////////// // Test move of MovableType // Should be much faster with C++11 move. /////////////////////////////////////////// std::vector stdVectorMovableType; eastl::tuple_vector eaTupleVectorMovableType; TestMoveReallocate(stopwatch1, stdVectorMovableType); TestMoveReallocate(stopwatch2, eaTupleVectorMovableType); if(i == 1) Benchmark::AddResult("tuple_vector/reallocate", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); TestMoveErase(stopwatch1, stdVectorMovableType); TestMoveErase(stopwatch2, eaTupleVectorMovableType); if(i == 1) Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////////////////// // Test move of AutoRefCount // Should be much faster with C++11 move. /////////////////////////////////////////// std::vector > stdVectorAutoRefCount; eastl::tuple_vector > eaTupleVectorAutoRefCount; for(size_t a = 0; a < 2048; a++) { stdVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); eaTupleVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); } RefCounted::msAddRefCount = 0; RefCounted::msReleaseCount = 0; TestMoveErase(stopwatch1, stdVectorAutoRefCount); //EASTLTest_Printf("tuple_vector/erase std counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); RefCounted::msAddRefCount = 0; RefCounted::msReleaseCount = 0; TestMoveErase(stopwatch2, eaTupleVectorAutoRefCount); //EASTLTest_Printf("tuple_vector/erase EA counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); if(i == 1) Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); ////////////////////////////////////////////////////////////////////////// // Test various operations with "padded" data, to demonstrate access/modification of sparse data StdVectorUint64Padded stdVectorUint64Padded; EaTupleVectorUint64Padded eaTupleVectorUint64Padded; /////////////////////////////// // Test push_back /////////////////////////////// TestTuplePushBack(stopwatch1, stdVectorUint64Padded, intVector); TestTuplePushBack(stopwatch2, eaTupleVectorUint64Padded, intVector); if(i == 1) Benchmark::AddResult("tuple_vector/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test operator[]. /////////////////////////////// TestTupleBracket(stopwatch1, stdVectorUint64Padded); TestTupleBracket(stopwatch2, eaTupleVectorUint64Padded); if(i == 1) Benchmark::AddResult("tuple_vector/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test iteration via find(). /////////////////////////////// TestTupleFind(stopwatch1, stdVectorUint64Padded); TestTupleFind(stopwatch2, eaTupleVectorUint64Padded); TestTupleFind(stopwatch1, stdVectorUint64Padded); TestTupleFind(stopwatch2, eaTupleVectorUint64Padded); if(i == 1) Benchmark::AddResult("tuple_vector/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test sort /////////////////////////////// // Currently VC++ complains about our sort function decrementing std::iterator that is already at // begin(). In the strictest sense, that's a valid complaint, but we aren't testing std STL here. We // will want to revise our sort function eventually. #if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2) TestTupleSort(stopwatch1, stdVectorUint64Padded); TestTupleSort(stopwatch2, eaTupleVectorUint64Padded); if(i == 1) Benchmark::AddResult("tuple_vector/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); #endif /////////////////////////////// // Test insert /////////////////////////////// TestTupleInsert(stopwatch1, stdVectorUint64Padded); TestTupleInsert(stopwatch2, eaTupleVectorUint64Padded); if(i == 1) Benchmark::AddResult("tuple_vector/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); /////////////////////////////// // Test erase /////////////////////////////// TestTupleErase(stopwatch1, stdVectorUint64Padded); TestTupleErase(stopwatch2, eaTupleVectorUint64Padded); if(i == 1) Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); } } }