diff options
author | bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-05-14 14:09:24 +0000 |
---|---|---|
committer | bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-05-14 14:09:24 +0000 |
commit | 554875210043b34178f7ed6ac5bd682b1fad367b (patch) | |
tree | fdadeb167ef502f98784117a3c05378c72fceeec | |
parent | f105b109264f71dfb0bfd9977e6a5dd0a5a12f57 (diff) |
Add bench and test for SkRefCnt.
http://codereview.appspot.com/6195071/
This also adds a cross platform SkThread for testing purposes.
git-svn-id: http://skia.googlecode.com/svn/trunk@3921 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | bench/RefCntBench.cpp | 77 | ||||
-rw-r--r-- | gyp/bench.gypi | 1 | ||||
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | gyp/utils.gyp | 23 | ||||
-rw-r--r-- | src/ports/SkThread_none.cpp | 4 | ||||
-rw-r--r-- | src/utils/SkThreadUtils.h | 46 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_pthread.cpp | 105 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_pthread.h | 29 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_pthread_linux.cpp | 46 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_pthread_mach.cpp | 30 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_pthread_other.cpp | 12 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_win.cpp | 136 | ||||
-rw-r--r-- | src/utils/SkThreadUtils_win.h | 28 | ||||
-rw-r--r-- | tests/RefCntTest.cpp | 44 |
14 files changed, 581 insertions, 1 deletions
diff --git a/bench/RefCntBench.cpp b/bench/RefCntBench.cpp new file mode 100644 index 0000000000..44fb648f4e --- /dev/null +++ b/bench/RefCntBench.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkBenchmark.h" +#include "SkThread.h" +#include <memory> + +enum { + N = SkBENCHLOOP(1000000), + M = SkBENCHLOOP(2) +}; + +class RefCntBench_Stack : public SkBenchmark { +public: + RefCntBench_Stack(void* param) : INHERITED(param) { + } +protected: + virtual const char* onGetName() { + return "ref_cnt_stack"; + } + + virtual void onDraw(SkCanvas* canvas) { + for (int i = 0; i < N; ++i) { + SkRefCnt ref; + for (int j = 0; j < M; ++j) { + ref.ref(); + ref.unref(); + } + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +class PlacedRefCnt : public SkRefCnt { +public: + PlacedRefCnt() : SkRefCnt() { } + void operator delete(void *p) { } +}; + +class RefCntBench_Heap : public SkBenchmark { +public: + RefCntBench_Heap(void* param) : INHERITED(param) { + } +protected: + virtual const char* onGetName() { + return "ref_cnt_heap"; + } + + virtual void onDraw(SkCanvas* canvas) { + char memory[sizeof(PlacedRefCnt)]; + for (int i = 0; i < N; ++i) { + PlacedRefCnt* ref = new (memory) PlacedRefCnt(); + for (int j = 0; j < M; ++j) { + ref->ref(); + ref->unref(); + } + ref->unref(); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact0(void* p) { return new RefCntBench_Stack(p); } +static SkBenchmark* Fact1(void* p) { return new RefCntBench_Heap(p); } + +static BenchRegistry gReg01(Fact0); +static BenchRegistry gReg02(Fact1); + diff --git a/gyp/bench.gypi b/gyp/bench.gypi index 8499ca137f..221faed9d8 100644 --- a/gyp/bench.gypi +++ b/gyp/bench.gypi @@ -33,6 +33,7 @@ '../bench/PathIterBench.cpp', '../bench/PicturePlaybackBench.cpp', '../bench/RectBench.cpp', + '../bench/RefCntBench.cpp', '../bench/RegionBench.cpp', '../bench/RepeatTileBench.cpp', '../bench/ScalarBench.cpp', diff --git a/gyp/tests.gyp b/gyp/tests.gyp index e7d5724cda..ec850a4923 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -61,6 +61,7 @@ '../tests/Reader32Test.cpp', '../tests/ReadPixelsTest.cpp', '../tests/ReadWriteAlphaTest.cpp', + '../tests/RefCntTest.cpp', '../tests/RefDictTest.cpp', '../tests/RegionTest.cpp', '../tests/ScalarTest.cpp', diff --git a/gyp/utils.gyp b/gyp/utils.gyp index 2fca63fbfe..d2c413ecbc 100644 --- a/gyp/utils.gyp +++ b/gyp/utils.gyp @@ -56,6 +56,14 @@ '../src/utils/SkParsePath.cpp', '../src/utils/SkProxyCanvas.cpp', '../src/utils/SkSfntUtils.cpp', + '../src/utils/SkThreadUtils.h', + '../src/utils/SkThreadUtils_pthread.cpp', + '../src/utils/SkThreadUtils_pthread.h', + '../src/utils/SkThreadUtils_pthread_linux.cpp', + '../src/utils/SkThreadUtils_pthread_mach.cpp', + '../src/utils/SkThreadUtils_pthread_other.cpp', + '../src/utils/SkThreadUtils_win.cpp', + '../src/utils/SkThreadUtils_win.h', '../src/utils/SkUnitMappers.cpp', #mac @@ -87,6 +95,9 @@ '../include/utils/mac', ], }, + 'sources!': [ + '../src/utils/SkThreadUtils_pthread_other.cpp', + ], },{ #else if 'skia_os != "mac"' 'include_dirs!': [ '../include/utils/mac', @@ -94,6 +105,7 @@ 'sources!': [ '../include/utils/mac/SkCGUtils.h', '../src/utils/mac/SkCreateCGImageRef.cpp', + '../src/utils/SkThreadUtils_pthread_mach.cpp', ], }], [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', { @@ -103,10 +115,16 @@ '-lGLU', ], }, + 'sources!': [ + '../src/utils/SkThreadUtils_pthread_other.cpp', + ], },{ #else if 'skia_os not in ["linux", "freebsd", "openbsd", "solaris"]' 'include_dirs!': [ '../include/utils/unix', ], + 'sources!': [ + '../src/utils/SkThreadUtils_pthread_linux.cpp', + ], }], [ 'skia_os == "win"', { 'direct_dependent_settings': { @@ -114,6 +132,11 @@ '../include/utils/win', ], }, + 'sources!': [ + '../src/utils/SkThreadUtils_pthread.cpp', + '../src/utils/SkThreadUtils_pthread.h', + '../src/utils/SkThreadUtils_pthread_other.cpp', + ], },{ #else if 'skia_os != "win"' 'include_dirs!': [ '../include/utils/win', diff --git a/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp index 9e170cfb3d..a948a5410c 100644 --- a/src/ports/SkThread_none.cpp +++ b/src/ports/SkThread_none.cpp @@ -8,6 +8,7 @@ #include "SkThread.h" +#include "SkTLS.h" int32_t sk_atomic_inc(int32_t* addr) { int32_t value = *addr; @@ -25,9 +26,10 @@ SkMutex::SkMutex() {} SkMutex::~SkMutex() {} +#ifndef SK_USE_POSIX_THREADS void SkMutex::acquire() {} - void SkMutex::release() {} +#endif ////////////////////////////////////////////////////////////////////////// diff --git a/src/utils/SkThreadUtils.h b/src/utils/SkThreadUtils.h new file mode 100644 index 0000000000..b0c5044618 --- /dev/null +++ b/src/utils/SkThreadUtils.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkThreadUtils_DEFINED +#define SkThreadUtils_DEFINED + +#include "SkTypes.h" + +class SkThread : SkNoncopyable { +public: + typedef void (*entryPointProc)(void*); + + SkThread(entryPointProc entryPoint, void* data = NULL); + + /** + * Non-virtual, do not subclass. + */ + ~SkThread(); + + /** + * Starts the thread. Returns false if the thread could not be started. + */ + bool start(); + + /** + * Waits for the thread to finish. + * If the thread has not started, returns immediately. + */ + void join(); + + /** + * SkThreads with an affinity for the same processor will attempt to run cache + * locally with each other. SkThreads with an affinity for different processors + * will attempt to run on different cores. Returns false if the request failed. + */ + bool setProcessorAffinity(unsigned int processor); + +private: + void* fData; +}; + +#endif diff --git a/src/utils/SkThreadUtils_pthread.cpp b/src/utils/SkThreadUtils_pthread.cpp new file mode 100644 index 0000000000..17a2075ab0 --- /dev/null +++ b/src/utils/SkThreadUtils_pthread.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#include "SkThreadUtils.h" +#include "SkThreadUtils_pthread.h" + +#include <pthread.h> +#include <signal.h> + +SkThread_PThreadData::SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data) + : fPThread() + , fValidPThread(false) + , fParam(data) + , fEntryPoint(entryPoint) + , fStarted(false) +{ + pthread_mutex_init(&fStartMutex, NULL); + + pthread_cond_init(&fStartCondition, NULL); + + pthread_attr_init(&fAttr); + pthread_attr_setdetachstate(&fAttr, PTHREAD_CREATE_JOINABLE); +} +SkThread_PThreadData::~SkThread_PThreadData() { + pthread_attr_destroy(&fAttr); + pthread_cond_destroy(&fStartCondition); + pthread_mutex_destroy(&fStartMutex); +} + +static void* thread_start(void* arg) { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(arg); + //Wait for start signal + pthread_mutex_lock(&(pthreadData->fStartMutex)); + while (!pthreadData->fStarted) { + pthread_cond_wait(&(pthreadData->fStartCondition), &(pthreadData->fStartMutex)); + } + pthread_mutex_unlock(&(pthreadData->fStartMutex)); + + //See if this thread was canceled before starting. + pthread_testcancel(); + + pthreadData->fEntryPoint(pthreadData->fParam); + return NULL; +} + +SkThread::SkThread(entryPointProc entryPoint, void* data) { + SkThread_PThreadData* pthreadData = new SkThread_PThreadData(entryPoint, data); + fData = pthreadData; + + int ret = pthread_create(&(pthreadData->fPThread), + &(pthreadData->fAttr), + thread_start, + pthreadData); + + pthreadData->fValidPThread = (0 == ret); +} + +SkThread::~SkThread() { + if (fData != NULL) { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); + // If created thread but start was never called, kill the thread. + if (pthreadData->fValidPThread && !pthreadData->fStarted) { + if (pthread_cancel(pthreadData->fPThread) == 0) { + if (this->start()) { + this->join(); + } + } else { + //kill with prejudice + pthread_kill(pthreadData->fPThread, SIGKILL); + } + } + delete pthreadData; + } +} + +bool SkThread::start() { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); + if (!pthreadData->fValidPThread) { + return false; + } + + if (pthreadData->fStarted) { + return false; + } + pthreadData->fStarted = true; + pthread_mutex_lock(&(pthreadData->fStartMutex)); + pthread_cond_signal(&(pthreadData->fStartCondition)); + pthread_mutex_unlock(&(pthreadData->fStartMutex)); + return true; +} + +void SkThread::join() { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); + if (!pthreadData->fValidPThread || !pthreadData->fStarted) { + return; + } + + pthread_join(pthreadData->fPThread, NULL); +} diff --git a/src/utils/SkThreadUtils_pthread.h b/src/utils/SkThreadUtils_pthread.h new file mode 100644 index 0000000000..52b398c2e2 --- /dev/null +++ b/src/utils/SkThreadUtils_pthread.h @@ -0,0 +1,29 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkThreadUtils_PThreadData_DEFINED +#define SkThreadUtils_PThreadData_DEFINED + +#include "SkThreadUtils.h" +#include <pthread.h> + +class SkThread_PThreadData { +public: + SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data); + ~SkThread_PThreadData(); + pthread_t fPThread; + bool fValidPThread; + pthread_mutex_t fStartMutex; + pthread_cond_t fStartCondition; + pthread_attr_t fAttr; + + void* fParam; + SkThread::entryPointProc fEntryPoint; + bool fStarted; +}; + +#endif diff --git a/src/utils/SkThreadUtils_pthread_linux.cpp b/src/utils/SkThreadUtils_pthread_linux.cpp new file mode 100644 index 0000000000..4a03cb8276 --- /dev/null +++ b/src/utils/SkThreadUtils_pthread_linux.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE //for pthread_setaffinity_np +#endif + +#include "SkThreadUtils.h" +#include "SkThreadUtils_pthread.h" + +#include <pthread.h> + +static int nth_set_cpu(unsigned int n, cpu_set_t* cpuSet) { + n %= CPU_COUNT(cpuSet); + for (unsigned int setCpusSeen = 0, currentCpu = 0; true; ++currentCpu) { + if (CPU_ISSET(currentCpu, cpuSet)) { + ++setCpusSeen; + if (setCpusSeen > n) { + return currentCpu; + } + } + } +} + +bool SkThread::setProcessorAffinity(unsigned int processor) { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); + if (!pthreadData->fValidPThread) { + return false; + } + + cpu_set_t parentCpuset; + if (0 != pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &parentCpuset)) { + return false; + } + + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(nth_set_cpu(processor, &parentCpuset), &cpuset); + return 0 == pthread_setaffinity_np(pthreadData->fPThread, + sizeof(cpu_set_t), + &cpuset); +} diff --git a/src/utils/SkThreadUtils_pthread_mach.cpp b/src/utils/SkThreadUtils_pthread_mach.cpp new file mode 100644 index 0000000000..0f6e263906 --- /dev/null +++ b/src/utils/SkThreadUtils_pthread_mach.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkThreadUtils.h" +#include "SkThreadUtils_pthread.h" + +#include <mach/mach.h> +#include <mach/thread_policy.h> +#include <pthread.h> + +bool SkThread::setProcessorAffinity(unsigned int processor) { + SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); + if (!pthreadData->fValidPThread) { + return false; + } + + mach_port_t tid = pthread_mach_thread_np(pthreadData->fPThread); + + thread_affinity_policy_data_t policy; + policy.affinity_tag = processor; + + return 0 == thread_policy_set(tid, + THREAD_AFFINITY_POLICY, + (thread_policy_t) &policy, + THREAD_AFFINITY_POLICY_COUNT); +} diff --git a/src/utils/SkThreadUtils_pthread_other.cpp b/src/utils/SkThreadUtils_pthread_other.cpp new file mode 100644 index 0000000000..a3973f1d72 --- /dev/null +++ b/src/utils/SkThreadUtils_pthread_other.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkThreadUtils.h" + +bool SkThread::setProcessorAffinity(unsigned int processor) { + return false; +} diff --git a/src/utils/SkThreadUtils_win.cpp b/src/utils/SkThreadUtils_win.cpp new file mode 100644 index 0000000000..208ffded1b --- /dev/null +++ b/src/utils/SkThreadUtils_win.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#include "SkThreadUtils.h" +#include "SkThreadUtils_win.h" + +SkThread_WinData::SkThread_WinData(SkThread::entryPointProc entryPoint, void* data) + : fHandle(NULL) + , fParam(data) + , fThreadId(0) + , fEntryPoint(entryPoint) + , fStarted(false) +{ + fCancelEvent = CreateEvent( + NULL, // default security attributes + false, //auto reset + false, //not signaled + NULL); //no name +} + +SkThread_WinData::~SkThread_WinData() { + CloseHandle(fCancelEvent); +} + +static DWORD WINAPI thread_start(LPVOID data) { + SkThread_WinData* winData = static_cast<SkThread_WinData*>(data); + + //See if this thread was canceled before starting. + if (WaitForSingleObject(winData->fCancelEvent, 0) == WAIT_OBJECT_0) { + return 0; + } + + winData->fEntryPoint(winData->fParam); + return 0; +} + +SkThread::SkThread(entryPointProc entryPoint, void* data) { + SkThread_WinData* winData = new SkThread_WinData(entryPoint, data); + fData = winData; + + if (NULL == winData->fCancelEvent) { + return; + } + + winData->fHandle = CreateThread( + NULL, // default security attributes + 0, // use default stack size + thread_start, // thread function name (proxy) + winData, // argument to thread function (proxy args) + CREATE_SUSPENDED, // create suspended so affinity can be set + &winData->fThreadId); // returns the thread identifier +} + +SkThread::~SkThread() { + if (fData != NULL) { + SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData); + // If created thread but start was never called, kill the thread. + if (winData->fHandle != NULL && !winData->fStarted) { + if (SetEvent(winData->fCancelEvent) != 0) { + if (this->start()) { + this->join(); + } + } else { + //kill with prejudice + TerminateThread(winData->fHandle, -1); + } + } + delete winData; + } +} + +bool SkThread::start() { + SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData); + if (NULL == winData->fHandle) { + return false; + } + + if (winData->fStarted) { + return false; + } + winData->fStarted = -1 != ResumeThread(winData->fHandle); + return winData->fStarted; +} + +void SkThread::join() { + SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData); + if (NULL == winData->fHandle || !winData->fStarted) { + return; + } + + WaitForSingleObject(winData->fHandle, INFINITE); +} + +static unsigned int num_bits_set(DWORD_PTR mask) { + unsigned int count; + for (count = 0; mask; ++count) { + mask &= mask - 1; + } + return count; +} + +static unsigned int nth_set_bit(unsigned int n, DWORD_PTR mask) { + n %= num_bits_set(mask); + for (unsigned int setBitsSeen = 0, currentBit = 0; true; ++currentBit) { + if (mask & (1 << currentBit)) { + ++setBitsSeen; + if (setBitsSeen > n) { + return currentBit; + } + } + } +} + +bool SkThread::setProcessorAffinity(unsigned int processor) { + SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData); + if (NULL == winData->fHandle) { + return false; + } + + DWORD_PTR processAffinityMask; + DWORD_PTR systemAffinityMask; + if (0 == GetProcessAffinityMask(GetCurrentProcess(), + &processAffinityMask, + &systemAffinityMask)) { + return false; + } + + DWORD_PTR threadAffinityMask = 1 << nth_set_bit(processor, processAffinityMask); + return 0 != SetThreadAffinityMask(winData->fHandle, threadAffinityMask); +} diff --git a/src/utils/SkThreadUtils_win.h b/src/utils/SkThreadUtils_win.h new file mode 100644 index 0000000000..5861e5d0db --- /dev/null +++ b/src/utils/SkThreadUtils_win.h @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkThreadUtils_WinData_DEFINED +#define SkThreadUtils_WinData_DEFINED + +#include "SkTypes.h" + +#include "SkThreadUtils.h" + +class SkThread_WinData { +public: + SkThread_WinData(SkThread::entryPointProc entryPoint, void* data); + ~SkThread_WinData(); + HANDLE fHandle; + HANDLE fCancelEvent; + + LPVOID fParam; + DWORD fThreadId; + SkThread::entryPointProc fEntryPoint; + bool fStarted; +}; + +#endif diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp new file mode 100644 index 0000000000..e48fd8a292 --- /dev/null +++ b/tests/RefCntTest.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#include "SkRefCnt.h" +#include "SkThreadUtils.h" + +/////////////////////////////////////////////////////////////////////////////// + +static void bounce_ref(void* data) { + SkRefCnt* ref = static_cast<SkRefCnt*>(data); + for (int i = 0; i < 100000; ++i) { + ref->ref(); + ref->unref(); + } +} + +static void test_refCnt(skiatest::Reporter* reporter) { + SkRefCnt* ref = new SkRefCnt(); + + SkThread thing1(bounce_ref, ref); + SkThread thing2(bounce_ref, ref); + + thing1.setProcessorAffinity(0); + thing2.setProcessorAffinity(23); + + SkASSERT(thing1.start()); + SkASSERT(thing2.start()); + + thing1.join(); + thing2.join(); + + REPORTER_ASSERT(reporter, ref->getRefCnt() == 1); + ref->unref(); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("ref_cnt", RefCntTestClass, test_refCnt) |