threadTestWithLocksCmd.cpp
#include <math.h>
#include <maya/MIOStream.h>
#include <maya/MSimple.h>
#include <maya/MTimer.h>
#include <maya/MGlobal.h>
#include <maya/MThreadAsync.h>
#include <maya/MThreadPool.h>
#include <maya/MSpinLock.h>
#include <maya/MMutexLock.h>
DeclareSimpleCommand( threadTestWithLocksCmd, PLUGIN_COMPANY, "2008");
#define NUM_TASKS 32
#define NUM_ASYNC_TASKS 13
MThreadRetVal Pi(void *data);
typedef struct _threadDataTag
{
int threadNo;
double pi, step, *globalPi;
int start, end;
MSpinLock *spinLock;
MMutexLock *mutexLock;
}threadData;
typedef struct _taskDataTag
{
int iterations;
double totalPi;
}taskData;
double CalcPiSingleThreaded(int iterations)
{
double pi = 0;
double step = 1.0/double(iterations);
for( int i = 0; i < iterations; i++ )
{
double x = step * ((double)i-0.5);
pi += 4.0 / (1.0 + x*x);
}
pi /= double(iterations);
return pi;
}
MThreadRetVal Pi(void *data)
{
threadData *myData = (threadData *)data;
double pi = 0;
for( int i = myData->start; i < myData->end; i++ )
{
double x = myData->step * ((double)i-0.5);
pi += 4.0 / (1.0 + x*x);
}
myData->pi = pi;
return (MThreadRetVal)0;
}
void DecomposePi(void *data, MThreadRootTask *root)
{
taskData *taskD = (taskData *)data;
int iterationsPerTask = taskD->iterations/NUM_TASKS;
int limit;
threadData tdata[NUM_TASKS];
if( tdata )
{
double step = 1.0f/taskD->iterations;
for( int i = 0; i < NUM_TASKS; ++i )
{
limit = (i+1)*iterationsPerTask;
tdata[i].threadNo = i;
tdata[i].pi = 0;
tdata[i].start = i*iterationsPerTask;
limit = tdata[i].start + iterationsPerTask;
tdata[i].end = ( limit < taskD->iterations) ? limit : taskD->iterations;
tdata[i].step = step;
MThreadPool::createTask(Pi, (void *)&tdata[i], root);
}
MThreadPool::executeAndJoin(root);
for( int i = 0; i < NUM_TASKS; ++i )
{
taskD->totalPi += tdata[i].pi;
}
}
}
double CalcPi(int iterations)
{
double Pi = 0;
taskData tdata;
tdata.iterations = 0; tdata.totalPi = 0;
MStatus pr = MThreadPool::init();
if( MStatus::kSuccess == pr )
{
tdata.iterations = iterations;
tdata.totalPi = 0;
MThreadPool::newParallelRegion(DecomposePi, (void *)&tdata);
Pi = tdata.totalPi/iterations;
MThreadPool::release();
}
MThreadPool::release();
return Pi;
}
MThreadRetVal SpinSyncPi(void *data)
{
threadData *myData = (threadData *)data;
double pi = 0;
for( int i = myData->start; i < myData->end; i++ )
{
double x = myData->step * ((double)i-0.5);
pi += 4.0 / (1.0 + x*x);
}
myData->spinLock->lock();
(*myData->globalPi) += pi;
myData->spinLock->unlock();
return 0;
}
void DecomposeSpinSyncPi(void *data, MThreadRootTask *root)
{
taskData *taskD = (taskData *)data;
int iterationsPerTask = taskD->iterations/NUM_TASKS;
int limit;
MStatus pr = MStatus::kSuccess;
MSpinLock* spinLock = new MSpinLock();
if( MStatus::kSuccess == pr )
{
threadData tdata[NUM_TASKS];
if( tdata )
{
double step = 1.0f/taskD->iterations;
for( int i = 0; i < NUM_TASKS; ++i )
{
limit = (i+1)*iterationsPerTask;
tdata[i].threadNo = i;
tdata[i].pi = 0;
tdata[i].globalPi = &taskD->totalPi;
tdata[i].start = i*iterationsPerTask;
limit = tdata[i].start + iterationsPerTask;
tdata[i].end = ( limit < taskD->iterations) ? limit : taskD->iterations;
tdata[i].step = step;
tdata[i].spinLock = spinLock;
MThreadPool::createTask(SpinSyncPi, (void *)&tdata[i], root);
}
MThreadPool::executeAndJoin(root);
for( int i = 0; i < NUM_TASKS; ++i )
{
taskD->totalPi += tdata[i].pi;
}
}
}
delete spinLock;
}
MThreadRetVal MutexSyncPi(void *data)
{
threadData *myData = (threadData *)data;
double pi = 0;
for( int i = myData->start; i < myData->end; i++ )
{
double x = myData->step * ((double)i-0.5);
pi += 4.0 / (1.0 + x*x);
}
myData->mutexLock->lock();
(*myData->globalPi) += pi;
myData->mutexLock->unlock();
return 0;
}
void DecomposeMutexSyncPi(void *data, MThreadRootTask *root)
{
taskData *taskD = (taskData *)data;
int iterationsPerTask = taskD->iterations/NUM_TASKS;
int limit;
MStatus pr = MStatus::kSuccess;
MMutexLock* mutexLock = new MMutexLock();
if( MStatus::kSuccess == pr )
{
threadData tdata[NUM_TASKS];
if( tdata )
{
double step = 1.0f/taskD->iterations;
for( int i = 0; i < NUM_TASKS; ++i )
{
limit = (i+1)*iterationsPerTask;
tdata[i].threadNo = i;
tdata[i].pi = 0;
tdata[i].globalPi = &taskD->totalPi;
tdata[i].start = i*iterationsPerTask;
limit = tdata[i].start + iterationsPerTask;
tdata[i].end = ( limit < taskD->iterations) ? limit : taskD->iterations;
tdata[i].step = step;
tdata[i].mutexLock = mutexLock;
MThreadPool::createTask(MutexSyncPi, (void *)&tdata[i], root);
}
MThreadPool::executeAndJoin(root);
for( int i = 0; i < NUM_TASKS; ++i )
{
taskD->totalPi += tdata[i].pi;
}
}
}
delete mutexLock;
}
double CalcSpinSyncPi(int iterations)
{
double Pi = 0;
taskData tdata;
MStatus pr = MThreadPool::init();
if( MStatus::kSuccess == pr )
{
tdata.iterations = iterations;
tdata.totalPi = 0;
MThreadPool::newParallelRegion(DecomposeSpinSyncPi, (void *)&tdata);
Pi = tdata.totalPi/iterations;
MThreadPool::release();
}
MThreadPool::release();
return Pi;
}
double CalcMutexSyncPi(int iterations)
{
double Pi = 0;
taskData tdata;
MStatus pr = MThreadPool::init();
if( MStatus::kSuccess == pr )
{
tdata.iterations = iterations;
tdata.totalPi = 0;
MThreadPool::newParallelRegion(DecomposeMutexSyncPi, (void *)&tdata);
Pi = tdata.totalPi/iterations;
MThreadPool::release();
}
MThreadPool::release();
return Pi;
}
MThreadRetVal AsyncPi(void *data)
{
threadData *myData = (threadData *)data;
for( int i = myData->start; i < myData->end; i++ )
{
double x = myData->step * ((double)i-0.5);
myData->pi += 4.0 / (1.0 + x*x);
}
return 0;
}
static volatile int g_async_count = 0;
static MSpinLock asyncSpinLock;
void AsyncCB(void *data)
{
asyncSpinLock.lock();
g_async_count++;
asyncSpinLock.unlock();
}
static MSpinLock exchangeSpinLock;
bool Maya_InterlockedCompare(volatile int* variable, int compareValue)
{
exchangeSpinLock.lock();
bool rtn = (*variable == compareValue);
exchangeSpinLock.unlock();
return rtn;
}
void WaitForAsyncThreads(int val)
{
while( !Maya_InterlockedCompare(&g_async_count, val)) {
#if defined(_WIN32)
Sleep(0);
#else
sleep(0);
#endif
}
}
double CalcAsyncPi(int iterations)
{
double Pi = 0;
g_async_count = 0;
MStatus pr = MThreadAsync::init();
if( MStatus::kSuccess == pr )
{
int iterationsPerTask = iterations/NUM_ASYNC_TASKS;
int limit;
threadData tdata[NUM_ASYNC_TASKS];
double step = 1.0f/iterations;
for( int i = 0; i < NUM_ASYNC_TASKS; ++i )
{
limit = (i+1)*iterationsPerTask;
tdata[i].threadNo = i;
tdata[i].pi = 0;
tdata[i].start = i*iterationsPerTask;
limit = tdata[i].start + iterationsPerTask;
tdata[i].end = ( limit < iterations) ? limit : iterations;
tdata[i].step = step;
pr = MThreadAsync::createTask(AsyncPi, (void *)&tdata[i],
AsyncCB, NULL);
if( pr != MStatus::kSuccess )
{
return 0;
}
}
WaitForAsyncThreads(NUM_ASYNC_TASKS);
for( int i = 0; i < NUM_ASYNC_TASKS; ++i )
{
Pi += tdata[i].pi;
}
Pi /= iterations;
MThreadAsync::release();
}
MThreadAsync::release();
return Pi;
}
MStatus threadTestWithLocksCmd::doIt( const MArgList& args )
{
MString str = MString("Computation of pi using the Maya API\n");
MGlobal::displayInfo(str);
if(args.length() != 1) {
MString str = MString("Invalid number of arguments, supply iteration count, usage: threadTestWithLocksCmd 100000");
MGlobal::displayError(str);
return MStatus::kFailure;
}
MStatus stat;
int iterations = args.asInt( 0, &stat );
if ( MS::kSuccess != stat ) {
MString str = MString("Invalid number of arguments, supply iteration count, usage: threadTestWithLocksCmd 100000");
MGlobal::displayError(str);
return MStatus::kFailure;
}
double pi = 0.0;
MString piStr;
double elapsedTime = 0.0;
MTimer timer;
timer.beginTimer();
pi = CalcPiSingleThreaded(iterations);
timer.endTimer();
elapsedTime = timer.elapsedTime();
piStr.set(pi, 10);
str = MString("Unthreaded computation, pi = ") + piStr + MString(" calculated in ") + elapsedTime + MString("s");
MGlobal::displayInfo(str);
timer.beginTimer();
pi = CalcPi(iterations);
timer.endTimer();
elapsedTime = timer.elapsedTime();
piStr.set(pi, 10);
str = MString("Threaded computation, pi = ") + piStr + MString(" calculated in ") + elapsedTime + MString("s");
MGlobal::displayInfo(str);
timer.beginTimer();
pi = CalcSpinSyncPi(iterations);
timer.endTimer();
elapsedTime = timer.elapsedTime();
piStr.set(pi, 10);
str = MString("Threaded computation with spin-lock synchronization, pi = ") + piStr + MString(" calculated in ") + elapsedTime + MString("s");
MGlobal::displayInfo(str);
timer.beginTimer();
pi = CalcMutexSyncPi(iterations);
timer.endTimer();
elapsedTime = timer.elapsedTime();
piStr.set(pi, 10);
str = MString("Threaded computation with mutex synchronization, pi = ") + piStr + MString(" calculated in ") + elapsedTime + MString("s");
MGlobal::displayInfo(str);
timer.beginTimer();
pi = CalcAsyncPi(iterations);
timer.endTimer();
elapsedTime = timer.elapsedTime();
piStr.set(pi, 10);
str = MString("Threaded computation with async thread, pi = ") + piStr + MString(" calculated in ") + elapsedTime + MString("s\n");
MGlobal::displayInfo(str);
return MStatus::kSuccess;
}