Bug 976848 - Add a 32-bit xorshift to ThreadPoolWorker for thread-local PRNG for workstealing. (r=nmatsakis)

This commit is contained in:
Shu-yu Guo 2014-02-26 19:51:28 -08:00
Родитель 5de4470f00
Коммит b4130cd003
2 изменённых файлов: 44 добавлений и 12 удалений

Просмотреть файл

@ -33,6 +33,14 @@ DecomposeSliceBounds(uint32_t bounds, uint16_t *from, uint16_t *to)
MOZ_ASSERT(*from <= *to);
}
ThreadPoolWorker::ThreadPoolWorker(uint32_t workerId, uint32_t rngSeed, ThreadPool *pool)
: workerId_(workerId),
pool_(pool),
sliceBounds_(0),
state_(CREATED),
schedulerRNGState_(rngSeed)
{ }
bool
ThreadPoolWorker::hasWork() const
{
@ -101,6 +109,18 @@ ThreadPoolWorker::stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId)
return true;
}
ThreadPoolWorker *
ThreadPoolWorker::randomWorker()
{
// Perform 32-bit xorshift.
uint32_t x = schedulerRNGState_;
x ^= x << XORSHIFT_A;
x ^= x >> XORSHIFT_B;
x ^= x << XORSHIFT_C;
schedulerRNGState_ = x;
return pool_->workers_[x % pool_->numWorkers()];
}
bool
ThreadPoolWorker::start()
{
@ -195,14 +215,10 @@ ThreadPoolWorker::getSlice(ForkJoinContext *cx, uint16_t *sliceId)
if (!pool_->workStealing())
return false;
ThreadPoolWorker *victim;
do {
if (!pool_->hasWork())
return false;
// Add one to add the main thread into the mix.
victim = pool_->workers_[rand() % pool_->numWorkers()];
} while (!stealFrom(victim, sliceId));
} while (!stealFrom(randomWorker(), sliceId));
return true;
}
@ -276,6 +292,8 @@ ThreadPool::workStealing() const
return true;
}
extern uint64_t random_next(uint64_t *, int);
bool
ThreadPool::lazyStartWorkers(JSContext *cx)
{
@ -295,8 +313,10 @@ ThreadPool::lazyStartWorkers(JSContext *cx)
// Note that numWorkers() is the number of *desired* workers,
// but workers_.length() is the number of *successfully
// initialized* workers.
uint64_t rngState = 0;
for (uint32_t workerId = 0; workerId < numWorkers(); workerId++) {
ThreadPoolWorker *worker = cx->new_<ThreadPoolWorker>(workerId, this);
uint32_t rngSeed = uint32_t(random_next(&rngState, 32));
ThreadPoolWorker *worker = cx->new_<ThreadPoolWorker>(workerId, rngSeed, this);
if (!worker || !workers_.append(worker)) {
terminateWorkersAndReportOOM(cx);
return false;

Просмотреть файл

@ -48,6 +48,10 @@ class ThreadPoolWorker
CREATED, ACTIVE, TERMINATED
} state_;
// Per-worker scheduler RNG state used for picking a random worker during
// work stealing.
uint32_t schedulerRNGState_;
// The thread's main function.
static void HelperThreadMain(void *arg);
void helperLoop();
@ -57,13 +61,21 @@ class ThreadPoolWorker
bool popSliceBack(uint16_t *sliceId);
bool stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId);
// Get a worker at random from the pool using our own thread-local RNG
// state. This is a weak, but very fast, random function [1]. We choose
// [a,b,c] = 11,21,13.
//
// [1] http://www.jstatsoft.org/v08/i14/paper
public:
ThreadPoolWorker(uint32_t workerId, ThreadPool *pool)
: workerId_(workerId),
pool_(pool),
sliceBounds_(0),
state_(CREATED)
{ }
static const uint32_t XORSHIFT_A = 11;
static const uint32_t XORSHIFT_B = 21;
static const uint32_t XORSHIFT_C = 13;
private:
ThreadPoolWorker *randomWorker();
public:
ThreadPoolWorker(uint32_t workerId, uint32_t rngSeed, ThreadPool *pool);
uint32_t id() const { return workerId_; }
bool isMainThread() const { return id() == 0; }