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
This commit is contained in:
bungeman@google.com 2012-05-14 14:09:24 +00:00
parent f105b10926
commit 5548752100
14 changed files with 581 additions and 1 deletions

77
bench/RefCntBench.cpp Normal file
View File

@ -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);

View File

@ -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',

View File

@ -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',

View File

@ -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',

View File

@ -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
//////////////////////////////////////////////////////////////////////////

46
src/utils/SkThreadUtils.h Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

44
tests/RefCntTest.cpp Normal file
View File

@ -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)