Refactored ParallelWork for better performance
This commit is contained in:
parent
2aeecdec62
commit
0566f2bdd8
9 changed files with 156 additions and 87 deletions
|
@ -12,7 +12,7 @@ WidgetPreviewCanvas::WidgetPreviewCanvas(QWidget *parent) :
|
||||||
pixbuf = new QImage();
|
pixbuf = new QImage();
|
||||||
inited = false;
|
inited = false;
|
||||||
|
|
||||||
startTimer(1000);
|
startTimer(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetPreviewCanvas::~WidgetPreviewCanvas()
|
WidgetPreviewCanvas::~WidgetPreviewCanvas()
|
||||||
|
@ -65,5 +65,6 @@ void WidgetPreviewCanvas::timerEvent(QTimerEvent *)
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas->getPreview()->updateLive(this);
|
canvas->getPreview()->updateLive(this);
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,8 @@ void SoftwareCanvasRenderer::postProcess(CanvasPortion *portion, bool threaded)
|
||||||
{
|
{
|
||||||
// Subdivide in chunks
|
// Subdivide in chunks
|
||||||
int chunk_size = 32;
|
int chunk_size = 32;
|
||||||
int chunks_x = portion->getWidth() / chunk_size + 1;
|
int chunks_x = (portion->getWidth() - 1) / chunk_size + 1;
|
||||||
int chunks_y = portion->getHeight() / chunk_size + 1;
|
int chunks_y = (portion->getHeight() - 1) / chunk_size + 1;
|
||||||
int units = chunks_x * chunks_y;
|
int units = chunks_x * chunks_y;
|
||||||
|
|
||||||
// Render chunks in parallel
|
// Render chunks in parallel
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
|
#include "Semaphore.h"
|
||||||
|
#include "Mutex.h"
|
||||||
#include "ParallelWorker.h"
|
#include "ParallelWorker.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -27,6 +29,56 @@ private:
|
||||||
void* data;
|
void* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread sub-class to perform units of work
|
||||||
|
*/
|
||||||
|
class ParallelWork::ParallelThread:public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParallelThread(ParallelWork *work):
|
||||||
|
Thread(), work(work), sem(0)
|
||||||
|
{
|
||||||
|
interrupted = false;
|
||||||
|
unit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void feedUnit(int unit)
|
||||||
|
{
|
||||||
|
this->unit = unit;
|
||||||
|
sem.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void interrupt()
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
sem.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void run() override
|
||||||
|
{
|
||||||
|
while (unit >= 0 or not interrupted)
|
||||||
|
{
|
||||||
|
// Wait for a unit (or interrupt)
|
||||||
|
sem.acquire();
|
||||||
|
|
||||||
|
// Process the unit
|
||||||
|
if (unit >= 0)
|
||||||
|
{
|
||||||
|
work->worker->processParallelUnit(unit);
|
||||||
|
work->returnThread(this);
|
||||||
|
unit = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParallelWork* work;
|
||||||
|
Semaphore sem;
|
||||||
|
int unit;
|
||||||
|
bool interrupted;
|
||||||
|
};
|
||||||
|
|
||||||
ParallelWork::ParallelWork(ParallelWorker *worker, int units)
|
ParallelWork::ParallelWork(ParallelWorker *worker, int units)
|
||||||
{
|
{
|
||||||
this->units = units;
|
this->units = units;
|
||||||
|
@ -52,58 +104,12 @@ ParallelWork::~ParallelWork()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* _workerThreadCallback(ParallelWork::ParallelWorkerThread* thread)
|
|
||||||
{
|
|
||||||
thread->result = thread->worker->processParallelUnit(thread->unit);
|
|
||||||
thread->status = ParallelWork::PARALLEL_WORKER_STATUS_DONE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _runNextWorker(ParallelWork::ParallelWorkerThread threads[], int thread_count, int unit)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
for (i = 0; i < thread_count; i++)
|
|
||||||
{
|
|
||||||
ParallelWork::ParallelWorkerThread* worker = threads + i;
|
|
||||||
if (worker->status == ParallelWork::PARALLEL_WORKER_STATUS_VOID)
|
|
||||||
{
|
|
||||||
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_RUNNING;
|
|
||||||
worker->result = 0;
|
|
||||||
worker->unit = unit;
|
|
||||||
worker->thread = new Thread((ThreadFunction)_workerThreadCallback);
|
|
||||||
worker->thread->start(worker);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (worker->status == ParallelWork::PARALLEL_WORKER_STATUS_DONE)
|
|
||||||
{
|
|
||||||
int result = worker->result;
|
|
||||||
|
|
||||||
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_RUNNING;
|
|
||||||
worker->result = 0;
|
|
||||||
worker->unit = unit;
|
|
||||||
worker->thread->join();
|
|
||||||
delete worker->thread;
|
|
||||||
worker->thread = new Thread((ThreadFunction)_workerThreadCallback);
|
|
||||||
worker->thread->start(worker);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Thread::timeSleepMs(50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ParallelWork::perform(int thread_count)
|
int ParallelWork::perform(int thread_count)
|
||||||
{
|
{
|
||||||
int i, done, result;
|
int i, done;
|
||||||
assert(not running);
|
assert(not running);
|
||||||
|
|
||||||
result = 0;
|
// Get thread count
|
||||||
|
|
||||||
if (thread_count <= 0)
|
if (thread_count <= 0)
|
||||||
{
|
{
|
||||||
thread_count = System::getCoreCount();
|
thread_count = System::getCoreCount();
|
||||||
|
@ -112,38 +118,65 @@ int ParallelWork::perform(int thread_count)
|
||||||
{
|
{
|
||||||
thread_count = PARALLEL_MAX_THREADS;
|
thread_count = PARALLEL_MAX_THREADS;
|
||||||
}
|
}
|
||||||
|
this->thread_count = thread_count;
|
||||||
running = 1;
|
running = 1;
|
||||||
|
|
||||||
/* Init workers */
|
// Init threads
|
||||||
|
semaphore = new Semaphore(thread_count);
|
||||||
|
mutex = new Mutex();
|
||||||
|
threads = new ParallelThread*[thread_count];
|
||||||
|
available = new ParallelThread*[thread_count];
|
||||||
|
available_offset = 0;
|
||||||
|
available_length = thread_count;
|
||||||
for (i = 0; i < thread_count; i++)
|
for (i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
threads[i].status = PARALLEL_WORKER_STATUS_VOID;
|
threads[i] = available[i] = new ParallelThread(this);
|
||||||
threads[i].worker = worker;
|
threads[i]->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform run */
|
// Perform all units
|
||||||
for (done = 0; done < units; done++)
|
for (done = 0; done < units; done++)
|
||||||
{
|
{
|
||||||
if (_runNextWorker(threads, thread_count, done))
|
// Take first available thread
|
||||||
|
semaphore->acquire();
|
||||||
|
mutex->acquire();
|
||||||
|
ParallelThread *thread = available[available_offset];
|
||||||
|
available_offset++;
|
||||||
|
if (available_offset >= thread_count)
|
||||||
{
|
{
|
||||||
result++;
|
available_offset = 0;
|
||||||
}
|
}
|
||||||
|
available_length--;
|
||||||
|
mutex->release();
|
||||||
|
|
||||||
|
// Feed the unit to it
|
||||||
|
thread->feedUnit(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait and clean up workers */
|
// Wait for all threads to end, then cleanup
|
||||||
for (i = 0; i < thread_count; i++)
|
for (i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
if (threads[i].status != PARALLEL_WORKER_STATUS_VOID)
|
threads[i]->interrupt();
|
||||||
|
}
|
||||||
|
for (i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
threads[i].thread->join();
|
threads[i]->join();
|
||||||
delete threads[i].thread;
|
delete threads[i];
|
||||||
if (threads[i].result)
|
|
||||||
{
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
delete[] threads;
|
||||||
|
delete[] available;
|
||||||
|
delete semaphore;
|
||||||
|
delete mutex;
|
||||||
|
|
||||||
running = 0;
|
running = 0;
|
||||||
return result;
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelWork::returnThread(ParallelWork::ParallelThread *thread)
|
||||||
|
{
|
||||||
|
mutex->acquire();
|
||||||
|
available[(available_offset + available_length) % thread_count] = thread;
|
||||||
|
available_length++;
|
||||||
|
semaphore->release();
|
||||||
|
mutex->release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,21 +13,11 @@ class SYSTEMSHARED_EXPORT ParallelWork
|
||||||
public:
|
public:
|
||||||
typedef int (*ParallelUnitFunction)(ParallelWork* work, int unit, void* data);
|
typedef int (*ParallelUnitFunction)(ParallelWork* work, int unit, void* data);
|
||||||
|
|
||||||
typedef enum
|
/**
|
||||||
{
|
* Obscure thread class.
|
||||||
PARALLEL_WORKER_STATUS_VOID,
|
*/
|
||||||
PARALLEL_WORKER_STATUS_RUNNING,
|
class ParallelThread;
|
||||||
PARALLEL_WORKER_STATUS_DONE
|
friend class ParallelThread;
|
||||||
} ParallelWorkerStatus;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
Thread* thread;
|
|
||||||
ParallelWorker* worker;
|
|
||||||
ParallelWorkerStatus status;
|
|
||||||
int unit;
|
|
||||||
int result;
|
|
||||||
} ParallelWorkerThread;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -58,11 +48,22 @@ public:
|
||||||
*/
|
*/
|
||||||
int perform(int thread_count=-1);
|
int perform(int thread_count=-1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void returnThread(ParallelThread *thread);
|
||||||
|
|
||||||
|
private:
|
||||||
int units;
|
int units;
|
||||||
int running;
|
int running;
|
||||||
ParallelWorker *worker;
|
ParallelWorker *worker;
|
||||||
bool worker_compat;
|
bool worker_compat;
|
||||||
ParallelWorkerThread threads[PARALLEL_MAX_THREADS];
|
|
||||||
|
int thread_count;
|
||||||
|
Mutex* mutex;
|
||||||
|
Semaphore* semaphore;
|
||||||
|
ParallelThread** threads;
|
||||||
|
ParallelThread** available;
|
||||||
|
int available_offset;
|
||||||
|
int available_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
6
src/system/Semaphore.cpp
Normal file
6
src/system/Semaphore.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "Semaphore.h"
|
||||||
|
|
||||||
|
Semaphore::Semaphore(int resources):
|
||||||
|
QSemaphore(resources)
|
||||||
|
{
|
||||||
|
}
|
25
src/system/Semaphore.h
Normal file
25
src/system/Semaphore.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SEMAPHORE_H
|
||||||
|
#define SEMAPHORE_H
|
||||||
|
|
||||||
|
#include "system_global.h"
|
||||||
|
|
||||||
|
#include <QSemaphore>
|
||||||
|
|
||||||
|
namespace paysages
|
||||||
|
{
|
||||||
|
namespace system
|
||||||
|
{
|
||||||
|
|
||||||
|
class Semaphore: private QSemaphore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Semaphore(int resources);
|
||||||
|
|
||||||
|
inline void acquire() {QSemaphore::acquire();}
|
||||||
|
inline void release() {QSemaphore::release();}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SEMAPHORE_H
|
|
@ -29,7 +29,7 @@ public:
|
||||||
* \brief Start the thread
|
* \brief Start the thread
|
||||||
* \param data User data to pass to the threaded function
|
* \param data User data to pass to the threaded function
|
||||||
*/
|
*/
|
||||||
void start(void* data);
|
void start(void* data=0);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Wait for the thread to end, and collect its result.
|
* \brief Wait for the thread to end, and collect its result.
|
||||||
|
|
|
@ -26,7 +26,8 @@ SOURCES += \
|
||||||
PictureWriter.cpp \
|
PictureWriter.cpp \
|
||||||
Logs.cpp \
|
Logs.cpp \
|
||||||
ParallelPool.cpp \
|
ParallelPool.cpp \
|
||||||
ParallelWorker.cpp
|
ParallelWorker.cpp \
|
||||||
|
Semaphore.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
system_global.h \
|
system_global.h \
|
||||||
|
@ -42,7 +43,8 @@ HEADERS += \
|
||||||
PictureWriter.h \
|
PictureWriter.h \
|
||||||
Logs.h \
|
Logs.h \
|
||||||
ParallelPool.h \
|
ParallelPool.h \
|
||||||
ParallelWorker.h
|
ParallelWorker.h \
|
||||||
|
Semaphore.h
|
||||||
|
|
||||||
unix:!symbian {
|
unix:!symbian {
|
||||||
maemo5 {
|
maemo5 {
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace system {
|
||||||
class ParallelWorker;
|
class ParallelWorker;
|
||||||
class Thread;
|
class Thread;
|
||||||
class Mutex;
|
class Mutex;
|
||||||
|
class Semaphore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using namespace paysages::system;
|
using namespace paysages::system;
|
||||||
|
|
Loading…
Reference in a new issue