| /*------------------------------------------------------------------------- |
| * drawElements C++ Base Library |
| * ----------------------------- |
| * |
| * Copyright 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Fast ordered append-only container |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "deAppendList.hpp" |
| #include "deThread.hpp" |
| #include "deSpinBarrier.hpp" |
| #include "deSharedPtr.hpp" |
| |
| #include <vector> |
| #include <algorithm> |
| |
| namespace de |
| { |
| |
| namespace |
| { |
| |
| using std::vector; |
| |
| struct TestElem |
| { |
| deUint32 threadNdx; |
| deUint32 elemNdx; |
| |
| TestElem (deUint32 threadNdx_, deUint32 elemNdx_) |
| : threadNdx (threadNdx_) |
| , elemNdx (elemNdx_) |
| {} |
| |
| TestElem (void) |
| : threadNdx (0) |
| , elemNdx (0) |
| {} |
| }; |
| |
| struct SharedState |
| { |
| deUint32 numElements; |
| SpinBarrier barrier; |
| AppendList<TestElem> testList; |
| |
| SharedState (deUint32 numThreads, deUint32 numElements_, deUint32 numElementsHint) |
| : numElements (numElements_) |
| , barrier (numThreads) |
| , testList (numElementsHint) |
| {} |
| }; |
| |
| class TestThread : public Thread |
| { |
| public: |
| TestThread (SharedState* shared, deUint32 threadNdx) |
| : m_shared (shared) |
| , m_threadNdx (threadNdx) |
| {} |
| |
| void run (void) |
| { |
| const deUint32 syncPerElems = 10000; |
| |
| for (deUint32 elemNdx = 0; elemNdx < m_shared->numElements; elemNdx++) |
| { |
| if (elemNdx % syncPerElems == 0) |
| m_shared->barrier.sync(SpinBarrier::WAIT_MODE_AUTO); |
| |
| m_shared->testList.append(TestElem(m_threadNdx, elemNdx)); |
| } |
| } |
| |
| private: |
| SharedState* const m_shared; |
| const deUint32 m_threadNdx; |
| }; |
| |
| typedef SharedPtr<TestThread> TestThreadSp; |
| |
| void runAppendListTest (deUint32 numThreads, deUint32 numElements, deUint32 numElementsHint) |
| { |
| SharedState sharedState (numThreads, numElements, numElementsHint); |
| vector<TestThreadSp> threads (numThreads); |
| |
| for (deUint32 threadNdx = 0; threadNdx < numThreads; ++threadNdx) |
| { |
| threads[threadNdx] = TestThreadSp(new TestThread(&sharedState, threadNdx)); |
| threads[threadNdx]->start(); |
| } |
| |
| for (deUint32 threadNdx = 0; threadNdx < numThreads; ++threadNdx) |
| threads[threadNdx]->join(); |
| |
| DE_TEST_ASSERT(sharedState.testList.size() == (size_t)numElements*(size_t)numThreads); |
| |
| { |
| vector<deUint32> countByThread (numThreads); |
| |
| std::fill(countByThread.begin(), countByThread.end(), 0); |
| |
| for (AppendList<TestElem>::const_iterator elemIter = sharedState.testList.begin(); |
| elemIter != sharedState.testList.end(); |
| ++elemIter) |
| { |
| const TestElem& elem = *elemIter; |
| |
| DE_TEST_ASSERT(de::inBounds(elem.threadNdx, 0u, numThreads)); |
| DE_TEST_ASSERT(countByThread[elem.threadNdx] == elem.elemNdx); |
| |
| countByThread[elem.threadNdx] += 1; |
| } |
| |
| for (deUint32 threadNdx = 0; threadNdx < numThreads; ++threadNdx) |
| DE_TEST_ASSERT(countByThread[threadNdx] == numElements); |
| } |
| } |
| |
| class ObjCountElem |
| { |
| public: |
| ObjCountElem (int* liveCount) |
| : m_liveCount(liveCount) |
| { |
| *m_liveCount += 1; |
| } |
| |
| ~ObjCountElem (void) |
| { |
| *m_liveCount -= 1; |
| } |
| |
| ObjCountElem (const ObjCountElem& other) |
| : m_liveCount(other.m_liveCount) |
| { |
| *m_liveCount += 1; |
| } |
| |
| ObjCountElem& operator= (const ObjCountElem& other) |
| { |
| m_liveCount = other.m_liveCount; |
| *m_liveCount += 1; |
| return *this; |
| } |
| |
| private: |
| int* m_liveCount; |
| }; |
| |
| void runClearTest (deUint32 numElements1, deUint32 numElements2, deUint32 numElementsHint) |
| { |
| int liveCount = 0; |
| |
| { |
| de::AppendList<ObjCountElem> testList (numElementsHint); |
| |
| for (deUint32 ndx = 0; ndx < numElements1; ++ndx) |
| testList.append(ObjCountElem(&liveCount)); |
| |
| DE_TEST_ASSERT(liveCount == (int)numElements1); |
| |
| testList.clear(); |
| |
| DE_TEST_ASSERT(liveCount == 0); |
| |
| for (deUint32 ndx = 0; ndx < numElements2; ++ndx) |
| testList.append(ObjCountElem(&liveCount)); |
| |
| DE_TEST_ASSERT(liveCount == (int)numElements2); |
| } |
| |
| DE_TEST_ASSERT(liveCount == 0); |
| } |
| |
| } // anonymous |
| |
| void AppendList_selfTest (void) |
| { |
| // Single-threaded |
| runAppendListTest(1, 1000, 500); |
| runAppendListTest(1, 1000, 2000); |
| runAppendListTest(1, 35, 1); |
| |
| // Multi-threaded |
| runAppendListTest(2, 10000, 500); |
| runAppendListTest(2, 100, 10); |
| |
| if (deGetNumAvailableLogicalCores() >= 4) |
| { |
| runAppendListTest(4, 10000, 500); |
| runAppendListTest(4, 100, 10); |
| } |
| |
| // Dtor + clear() |
| runClearTest(1, 1, 1); |
| runClearTest(1, 2, 10); |
| runClearTest(50, 25, 10); |
| runClearTest(9, 50, 10); |
| runClearTest(10, 50, 10); |
| runClearTest(50, 9, 10); |
| runClearTest(50, 10, 10); |
| } |
| |
| } // de |