Bug 985312 - PJS: Remove the bounds function from ForkJoin. (r=nmatsakis)

--HG--
rename : js/src/builtin/Parallel.js => js/src/builtin/ParallelUtilities.js
This commit is contained in:
Shu-yu Guo 2014-03-26 05:30:22 -07:00
parent d767bb2e5a
commit 714c15c5d3
12 changed files with 254 additions and 331 deletions

View File

@ -366,6 +366,7 @@ selfhosting:: selfhosted.out.h
selfhosting_srcs := \ selfhosting_srcs := \
$(srcdir)/builtin/Utilities.js \ $(srcdir)/builtin/Utilities.js \
$(srcdir)/builtin/ParallelUtilities.js \
$(srcdir)/builtin/Array.js \ $(srcdir)/builtin/Array.js \
$(srcdir)/builtin/Date.js \ $(srcdir)/builtin/Date.js \
$(srcdir)/builtin/Intl.js \ $(srcdir)/builtin/Intl.js \
@ -373,7 +374,6 @@ selfhosting_srcs := \
$(srcdir)/builtin/Iterator.js \ $(srcdir)/builtin/Iterator.js \
$(srcdir)/builtin/Map.js \ $(srcdir)/builtin/Map.js \
$(srcdir)/builtin/Number.js \ $(srcdir)/builtin/Number.js \
$(srcdir)/builtin/Parallel.js \
$(srcdir)/builtin/String.js \ $(srcdir)/builtin/String.js \
$(srcdir)/builtin/Set.js \ $(srcdir)/builtin/Set.js \
$(srcdir)/builtin/TypedObject.js \ $(srcdir)/builtin/TypedObject.js \

View File

@ -595,7 +595,7 @@ function ArrayMapPar(func, mode) {
break parallel; break parallel;
var slicesInfo = ComputeSlicesInfo(length); var slicesInfo = ComputeSlicesInfo(length);
ForkJoin(mapThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
return buffer; return buffer;
} }
@ -605,17 +605,16 @@ function ArrayMapPar(func, mode) {
UnsafePutElements(buffer, i, func(self[i], i, self)); UnsafePutElements(buffer, i, func(self[i], i, self));
return buffer; return buffer;
function mapThread(_, warmup) { function mapThread(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
for (var i = indexStart; i < indexEnd; i++) for (var i = indexStart; i < indexEnd; i++)
UnsafePutElements(buffer, i, func(self[i], i, self)); UnsafePutElements(buffer, i, func(self[i], i, self));
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;
@ -642,10 +641,10 @@ function ArrayReducePar(func, mode) {
break parallel; break parallel;
var slicesInfo = ComputeSlicesInfo(length); var slicesInfo = ComputeSlicesInfo(length);
var numSlices = SLICE_COUNT(slicesInfo); var numSlices = slicesInfo.count;
var subreductions = NewDenseArray(numSlices); var subreductions = NewDenseArray(numSlices);
ForkJoin(reduceThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode));
var accumulator = subreductions[0]; var accumulator = subreductions[0];
for (var i = 1; i < numSlices; i++) for (var i = 1; i < numSlices; i++)
@ -660,19 +659,18 @@ function ArrayReducePar(func, mode) {
accumulator = func(accumulator, self[i]); accumulator = func(accumulator, self[i]);
return accumulator; return accumulator;
function reduceThread(_, warmup) { function reduceThread(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
var accumulator = self[indexStart]; var accumulator = self[indexStart];
for (var i = indexStart + 1; i < indexEnd; i++) for (var i = indexStart + 1; i < indexEnd; i++)
accumulator = func(accumulator, self[i]); accumulator = func(accumulator, self[i]);
UnsafePutElements(subreductions, sliceId, accumulator); UnsafePutElements(subreductions, sliceId, accumulator);
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;
@ -702,10 +700,10 @@ function ArrayScanPar(func, mode) {
break parallel; break parallel;
var slicesInfo = ComputeSlicesInfo(length); var slicesInfo = ComputeSlicesInfo(length);
var numSlices = SLICE_COUNT(slicesInfo); var numSlices = slicesInfo.count;
// Scan slices individually (see comment on phase1()). // Scan slices individually (see comment on phase1()).
ForkJoin(phase1, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(phase1, 0, numSlices, ForkJoinMode(mode));
// Compute intermediates array (see comment on phase2()). // Compute intermediates array (see comment on phase2()).
var intermediates = []; var intermediates = [];
@ -716,14 +714,12 @@ function ArrayScanPar(func, mode) {
ARRAY_PUSH(intermediates, accumulator); ARRAY_PUSH(intermediates, accumulator);
} }
// Clear the slices' statuses in between phases.
SlicesInfoClearStatuses(slicesInfo);
// There is no work to be done for slice 0, so mark it as done.
MARK_SLICE_DONE(slicesInfo, 0);
// Complete each slice using intermediates array (see comment on phase2()). // Complete each slice using intermediates array (see comment on phase2()).
ForkJoin(phase2, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); //
// We start from slice 1 instead of 0 since there is no work to be done
// for slice 0.
if (numSlices > 1)
ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode));
return buffer; return buffer;
} }
@ -757,23 +753,23 @@ function ArrayScanPar(func, mode) {
* *
* Read on in phase2 to see what we do next! * Read on in phase2 to see what we do next!
*/ */
function phase1(_, warmup) { function phase1(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
scan(self[indexStart], indexStart, indexEnd); scan(self[indexStart], indexStart, indexEnd);
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
/** /**
* Computes the index of the final element computed by the slice |sliceId|. * Computes the index of the final element computed by the slice |sliceId|.
*/ */
function finalElement(sliceId) { function finalElement(sliceId) {
return SLICE_END(slicesInfo, SLICE_START(slicesInfo, sliceId), length) - 1; var sliceShift = slicesInfo.shift;
return SLICE_END_INDEX(sliceShift, SLICE_START_INDEX(sliceShift, sliceId), length) - 1;
} }
/** /**
@ -809,20 +805,17 @@ function ArrayScanPar(func, mode) {
* result is [(A+B+C)+D, (A+B+C)+(D+E), (A+B+C)+(D+E+F)]. Again I * result is [(A+B+C)+D, (A+B+C)+(D+E), (A+B+C)+(D+E+F)]. Again I
* am using parentheses to clarify how these results were reduced. * am using parentheses to clarify how these results were reduced.
*/ */
function phase2(_, warmup) { function phase2(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexPos = SLICE_START(slicesInfo, sliceId); var indexPos = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexPos, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexPos, length);
var intermediate = intermediates[sliceId - 1]; var intermediate = intermediates[sliceId - 1];
for (; indexPos < indexEnd; indexPos++) for (; indexPos < indexEnd; indexPos++)
UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos])); UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos]));
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;
@ -937,15 +930,12 @@ function ArrayFilterPar(func, mode) {
// preserved from within one slice. // preserved from within one slice.
// //
// FIXME(bug 844890): Use typed arrays here. // FIXME(bug 844890): Use typed arrays here.
var numSlices = SLICE_COUNT(slicesInfo); var numSlices = slicesInfo.count;
var counts = NewDenseArray(numSlices); var counts = NewDenseArray(numSlices);
for (var i = 0; i < numSlices; i++) for (var i = 0; i < numSlices; i++)
UnsafePutElements(counts, i, 0); UnsafePutElements(counts, i, 0);
var survivors = NewDenseArray(computeNum32BitChunks(length)); var survivors = NewDenseArray(computeNum32BitChunks(length));
ForkJoin(findSurvivorsThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(findSurvivorsThread, 0, numSlices, ForkJoinMode(mode));
// Clear the slices' statuses in between phases.
SlicesInfoClearStatuses(slicesInfo);
// Step 2. Compress the slices into one contiguous set. // Step 2. Compress the slices into one contiguous set.
var count = 0; var count = 0;
@ -953,7 +943,7 @@ function ArrayFilterPar(func, mode) {
count += counts[i]; count += counts[i];
var buffer = NewDenseArray(count); var buffer = NewDenseArray(count);
if (count > 0) if (count > 0)
ForkJoin(copySurvivorsThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(copySurvivorsThread, 0, numSlices, ForkJoinMode(mode));
return buffer; return buffer;
} }
@ -984,12 +974,13 @@ function ArrayFilterPar(func, mode) {
* time. When we finish a chunk, we record our current count and * time. When we finish a chunk, we record our current count and
* the next chunk sliceId, lest we should bail. * the next chunk sliceId, lest we should bail.
*/ */
function findSurvivorsThread(_, warmup) { function findSurvivorsThread(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var count = 0; var count = 0;
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
var chunkStart = computeNum32BitChunks(indexStart); var chunkStart = computeNum32BitChunks(indexStart);
var chunkEnd = computeNum32BitChunks(indexEnd); var chunkEnd = computeNum32BitChunks(indexEnd);
for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) { for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) {
@ -1002,16 +993,14 @@ function ArrayFilterPar(func, mode) {
UnsafePutElements(survivors, chunkPos, chunkBits); UnsafePutElements(survivors, chunkPos, chunkBits);
} }
UnsafePutElements(counts, sliceId, count); UnsafePutElements(counts, sliceId, count);
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
function copySurvivorsThread(_, warmup) { function copySurvivorsThread(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
// Copies the survivors from this slice into the correct position. // Copies the survivors from this slice into the correct position.
// Note that this is an idempotent operation that does not invoke // Note that this is an idempotent operation that does not invoke
// user code. Therefore, we don't expect bailouts and make an // user code. Therefore, we don't expect bailouts and make an
@ -1024,18 +1013,16 @@ function ArrayFilterPar(func, mode) {
// Compute the final index we expect to write. // Compute the final index we expect to write.
var count = total - counts[sliceId]; var count = total - counts[sliceId];
if (count === total) { if (count === total)
MARK_SLICE_DONE(slicesInfo, sliceId);
continue; continue;
}
// Iterate over the chunks assigned to us. Read the bitset for // Iterate over the chunks assigned to us. Read the bitset for
// each chunk. Copy values where a 1 appears until we have // each chunk. Copy values where a 1 appears until we have
// written all the values that we expect to. We can just iterate // written all the values that we expect to. We can just iterate
// from 0...CHUNK_SIZE without fear of a truncated final chunk // from 0...CHUNK_SIZE without fear of a truncated final chunk
// because we are already checking for when count==total. // because we are already checking for when count==total.
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
var chunkStart = computeNum32BitChunks(indexStart); var chunkStart = computeNum32BitChunks(indexStart);
var chunkEnd = computeNum32BitChunks(indexEnd); var chunkEnd = computeNum32BitChunks(indexEnd);
for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) { for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) {
@ -1054,11 +1041,9 @@ function ArrayFilterPar(func, mode) {
if (count == total) if (count == total)
break; break;
} }
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;
@ -1101,7 +1086,7 @@ function ArrayStaticBuildPar(length, func, mode) {
break parallel; break parallel;
var slicesInfo = ComputeSlicesInfo(length); var slicesInfo = ComputeSlicesInfo(length);
ForkJoin(constructThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(constructThread, 0, slicesInfo.count, ForkJoinMode(mode));
return buffer; return buffer;
} }
@ -1111,17 +1096,16 @@ function ArrayStaticBuildPar(length, func, mode) {
UnsafePutElements(buffer, i, func(i)); UnsafePutElements(buffer, i, func(i));
return buffer; return buffer;
function constructThread(_, warmup) { function constructThread(workerId, sliceStart, sliceEnd) {
var sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
var indexStart = SLICE_START(slicesInfo, sliceId); var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
var indexEnd = SLICE_END(slicesInfo, indexStart, length); var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
for (var i = indexStart; i < indexEnd; i++) for (var i = indexStart; i < indexEnd; i++)
UnsafePutElements(buffer, i, func(i)); UnsafePutElements(buffer, i, func(i));
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;

View File

@ -1,67 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Shared utility functions for parallel operations in `Array.js`
// and `TypedObject.js`.
/**
* Determine the number and size of slices.
*/
function ComputeSlicesInfo(length) {
var count = length >>> MAX_SLICE_SHIFT;
var numWorkers = ForkJoinNumWorkers();
if (count < numWorkers)
count = numWorkers;
else if (count >= numWorkers * MAX_SLICES_PER_WORKER)
count = numWorkers * MAX_SLICES_PER_WORKER;
// Round the slice size to be a power of 2.
var shift = std_Math_max(std_Math_log2(length / count) | 0, 1);
// Recompute count with the rounded size.
count = length >>> shift;
if (count << shift !== length)
count += 1;
return { shift: shift, statuses: new Uint8Array(count), lastSequentialId: 0 };
}
/**
* Reset the status array of the slices info object.
*/
function SlicesInfoClearStatuses(info) {
var statuses = info.statuses;
var length = statuses.length;
for (var i = 0; i < length; i++)
UnsafePutElements(statuses, i, 0);
info.lastSequentialId = 0;
}
/**
* Compute the slice such that all slices before it (but not including it) are
* completed.
*/
function NextSequentialSliceId(info, doneMarker) {
var statuses = info.statuses;
var length = statuses.length;
for (var i = info.lastSequentialId; i < length; i++) {
if (statuses[i] === SLICE_STATUS_DONE)
continue;
info.lastSequentialId = i;
return i;
}
return doneMarker == undefined ? length : doneMarker;
}
/**
* Determinism-preserving bounds function.
*/
function ShrinkLeftmost(info) {
return function () {
return [NextSequentialSliceId(info), SLICE_COUNT(info)]
};
}

View File

@ -0,0 +1,74 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Shared utility functions for and macros parallel operations in `Array.js`
// and `TypedObject.js`.
#ifdef ENABLE_PARALLEL_JS
/* The mode asserts options object. */
#define TRY_PARALLEL(MODE) \
((!MODE || MODE.mode !== "seq"))
#define ASSERT_SEQUENTIAL_IS_OK(MODE) \
do { if (MODE) AssertSequentialIsOK(MODE) } while(false)
/**
* The ParallelSpew intrinsic is only defined in debug mode, so define a dummy
* if debug is not on.
*/
#ifndef DEBUG
#define ParallelSpew(args)
#endif
#define MAX_SLICE_SHIFT 6
#define MAX_SLICE_SIZE 64
#define MAX_SLICES_PER_WORKER 8
/**
* Macros to help compute the start and end indices of slices based on id. Use
* with the object returned by ComputeSliceInfo.
*/
#define SLICE_START_INDEX(shift, id) \
(id << shift)
#define SLICE_END_INDEX(shift, start, length) \
std_Math_min(start + (1 << shift), length)
/**
* ForkJoinGetSlice acts as identity when we are not in a parallel section, so
* pass in the next sequential value when we are in sequential mode. The
* reason for this odd API is because intrinsics *need* to be called during
* ForkJoin's warmup to fill the TI info.
*/
#define GET_SLICE(sliceStart, sliceEnd, id) \
((id = ForkJoinGetSlice((InParallelSection() ? -1 : sliceStart++) | 0)) < sliceEnd)
/**
* Determine the number and size of slices. The info object has the following
* properties:
*
* - shift: amount to shift by to compute indices
* - count: number of slices
* - seqSliceId: the slice id for which slices [0,id] have been run
* sequentially and cannot be re-run in parallel.
*/
function ComputeSlicesInfo(length) {
var count = length >>> MAX_SLICE_SHIFT;
var numWorkers = ForkJoinNumWorkers();
if (count < numWorkers)
count = numWorkers;
else if (count >= numWorkers * MAX_SLICES_PER_WORKER)
count = numWorkers * MAX_SLICES_PER_WORKER;
// Round the slice size to be a power of 2.
var shift = std_Math_max(std_Math_log2(length / count) | 0, 1);
// Recompute count with the rounded size.
count = length >>> shift;
if (count << shift !== length)
count += 1;
return { shift: shift, count: count };
}
#endif // ENABLE_PARALLEL_JS

View File

@ -1434,23 +1434,26 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
// relative to its owner (which is often but not always 0). // relative to its owner (which is often but not always 0).
const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray); const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray);
ForkJoin(mapThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode)); ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
return outArray; return outArray;
function mapThread(workerId, warmup) { function mapThread(workerId, sliceStart, sliceEnd) {
assert(TO_INT32(workerId) === workerId, assert(TO_INT32(workerId) === workerId,
"workerId not int: " + workerId); "workerId not int: " + workerId);
assert(workerId >= 0 && workerId < pointers.length, assert(workerId < pointers.length,
"workerId too large: " + workerId + " >= " + pointers.length); "workerId too large: " + workerId + " >= " + pointers.length);
assert(!!pointers[workerId],
var pointerIndex = InParallelSection() ? workerId : 0;
assert(!!pointers[pointerIndex],
"no pointer data for workerId: " + workerId); "no pointer data for workerId: " + workerId);
const { inTypedObject, outTypedObject } = pointers[pointerIndex];
const sliceShift = slicesInfo.shift;
var sliceId; var sliceId;
const { inTypedObject, outTypedObject } = pointers[workerId];
while (GET_SLICE(slicesInfo, sliceId)) { while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
const indexStart = SLICE_START(slicesInfo, sliceId); const indexStart = SLICE_START_INDEX(sliceShift, sliceId);
const indexEnd = SLICE_END(slicesInfo, indexStart, length); const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart); var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart);
var outOffset = std_Math_imul(outGrainTypeSize, indexStart); var outOffset = std_Math_imul(outGrainTypeSize, indexStart);
@ -1482,7 +1485,7 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
if (outGrainTypeIsComplex) if (outGrainTypeIsComplex)
SetTypedObjectValue(outGrainType, outArray, outOffset, r); SetTypedObjectValue(outGrainType, outArray, outOffset, r);
else else
UnsafePutElements(outArray, i, r); UnsafePutElements(outArray, i, r);
} }
inOffset += inGrainTypeSize; inOffset += inGrainTypeSize;
outOffset += outGrainTypeSize; outOffset += outGrainTypeSize;
@ -1493,11 +1496,9 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
// to escape. // to escape.
if (outGrainTypeIsTransparent) if (outGrainTypeIsTransparent)
ClearThreadLocalArenas(); ClearThreadLocalArenas();
MARK_SLICE_DONE(slicesInfo, sliceId);
if (warmup)
return;
} }
return sliceId;
} }
return undefined; return undefined;

View File

@ -96,57 +96,6 @@ var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
#define ARRAY_SLICE(ARRAY, ELEMENT) \ #define ARRAY_SLICE(ARRAY, ELEMENT) \
callFunction(std_Array_slice, ARRAY, ELEMENT); callFunction(std_Array_slice, ARRAY, ELEMENT);
/********** Parallel JavaScript macros and so on **********/
#ifdef ENABLE_PARALLEL_JS
/* The mode asserts options object. */
#define TRY_PARALLEL(MODE) \
((!MODE || MODE.mode !== "seq"))
#define ASSERT_SEQUENTIAL_IS_OK(MODE) \
do { if (MODE) AssertSequentialIsOK(MODE) } while(false)
/**
* The ParallelSpew intrinsic is only defined in debug mode, so define a dummy
* if debug is not on.
*/
#ifndef DEBUG
#define ParallelSpew(args)
#endif
#define MAX_SLICE_SHIFT 6
#define MAX_SLICE_SIZE 64
#define MAX_SLICES_PER_WORKER 8
/**
* Macros to help compute the start and end indices of slices based on id. Use
* with the object returned by ComputeSliceInfo.
*/
#define SLICE_START(info, id) \
(id << info.shift)
#define SLICE_END(info, start, length) \
std_Math_min(start + (1 << info.shift), length)
#define SLICE_COUNT(info) \
info.statuses.length
/**
* ForkJoinGetSlice acts as identity when we are not in a parallel section, so
* pass in the next sequential value when we are in sequential mode. The
* reason for this odd API is because intrinsics *need* to be called during
* ForkJoin's warmup to fill the TI info.
*/
#define GET_SLICE(info, id) \
((id = ForkJoinGetSlice(InParallelSection() ? -1 : NextSequentialSliceId(info, -1))) >= 0)
#define SLICE_STATUS_DONE 1
/**
* Macro to mark a slice as completed in the info object.
*/
#define MARK_SLICE_DONE(info, id) \
UnsafePutElements(info.statuses, id, SLICE_STATUS_DONE)
#endif // ENABLE_PARALLEL_JS
/********** List specification type **********/ /********** List specification type **********/

View File

@ -2010,9 +2010,9 @@ JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
masm.pop(cxReg); masm.pop(cxReg);
masm.ret(); masm.ret();
// There's no more slices to give out, return -1. // There's no more slices to give out, return a sentinel value.
masm.bind(&noMoreWork); masm.bind(&noMoreWork);
masm.move32(Imm32(-1), output); masm.move32(Imm32(ThreadPool::MAX_SLICE_ID), output);
masm.pop(cxReg); masm.pop(cxReg);
masm.ret(); masm.ret();

View File

@ -51,14 +51,20 @@ using mozilla::ThreadLocal;
// altogether. // altogether.
static bool static bool
ExecuteSequentially(JSContext *cx_, HandleValue funVal); ExecuteSequentially(JSContext *cx_, HandleValue funVal, uint16_t *sliceStart,
uint16_t sliceEnd);
#if !defined(JS_THREADSAFE) || !defined(JS_ION) #if !defined(JS_THREADSAFE) || !defined(JS_ION)
bool bool
js::ForkJoin(JSContext *cx, CallArgs &args) js::ForkJoin(JSContext *cx, CallArgs &args)
{ {
RootedValue argZero(cx, args[0]); RootedValue argZero(cx, args[0]);
return ExecuteSequentially(cx, argZero); uint16_t sliceStart = uint16_t(args[1].toInt32());
uint16_t sliceEnd = uint16_t(args[2].toInt32());
if (!ExecuteSequentially(cx, argZero, &sliceStart, sliceEnd))
return false;
MOZ_ASSERT(sliceStart == sliceEnd);
return true;
} }
JSContext * JSContext *
@ -184,17 +190,22 @@ JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_ClearThreadLocalArenasInfo,
// Some code that is shared between degenerate and parallel configurations. // Some code that is shared between degenerate and parallel configurations.
static bool static bool
ExecuteSequentially(JSContext *cx, HandleValue funVal) ExecuteSequentially(JSContext *cx, HandleValue funVal, uint16_t *sliceStart,
uint16_t sliceEnd)
{ {
FastInvokeGuard fig(cx, funVal); FastInvokeGuard fig(cx, funVal);
InvokeArgs &args = fig.args(); InvokeArgs &args = fig.args();
if (!args.init(2)) if (!args.init(3))
return false; return false;
args.setCallee(funVal); args.setCallee(funVal);
args.setThis(UndefinedValue()); args.setThis(UndefinedValue());
args[0].setInt32(0); // always worker 0 in seq args[0].setInt32(0);
args[1].setBoolean(!!cx->runtime()->forkJoinWarmup); args[1].setInt32(*sliceStart);
return fig.invoke(cx); args[2].setInt32(sliceEnd);
if (!fig.invoke(cx))
return false;
*sliceStart = (uint16_t)(args.rval().toInt32());
return true;
} }
ThreadLocal<ForkJoinContext*> ForkJoinContext::tlsForkJoinContext; ThreadLocal<ForkJoinContext*> ForkJoinContext::tlsForkJoinContext;
@ -267,8 +278,8 @@ class ForkJoinOperation
RootedScript bailoutScript; RootedScript bailoutScript;
jsbytecode *bailoutBytecode; jsbytecode *bailoutBytecode;
ForkJoinOperation(JSContext *cx, HandleFunction fun, HandleFunction boundsFun, ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
ForkJoinMode mode); uint16_t sliceEnd, ForkJoinMode mode);
ExecutionStatus apply(); ExecutionStatus apply();
private: private:
@ -307,7 +318,8 @@ class ForkJoinOperation
JSContext *cx_; JSContext *cx_;
HandleFunction fun_; HandleFunction fun_;
HandleFunction boundsFun_; uint16_t sliceStart_;
uint16_t sliceEnd_;
Vector<ParallelBailoutRecord, 16> bailoutRecords_; Vector<ParallelBailoutRecord, 16> bailoutRecords_;
AutoScriptVector worklist_; AutoScriptVector worklist_;
Vector<WorklistData, 16> worklistData_; Vector<WorklistData, 16> worklistData_;
@ -329,8 +341,6 @@ class ForkJoinOperation
TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status); TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
bool addToWorklist(HandleScript script); bool addToWorklist(HandleScript script);
inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script); inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
bool computeBounds(uint16_t *start, uint16_t *end);
}; // class ForkJoinOperation }; // class ForkJoinOperation
class ForkJoinShared : public ParallelJob, public Monitor class ForkJoinShared : public ParallelJob, public Monitor
@ -341,8 +351,8 @@ class ForkJoinShared : public ParallelJob, public Monitor
JSContext *const cx_; // Current context JSContext *const cx_; // Current context
ThreadPool *const threadPool_; // The thread pool ThreadPool *const threadPool_; // The thread pool
HandleFunction fun_; // The JavaScript function to execute HandleFunction fun_; // The JavaScript function to execute
uint16_t sliceFrom_; // The starting slice id. uint16_t sliceStart_; // The starting slice id.
uint16_t sliceTo_; // The ending slice id + 1. uint16_t sliceEnd_; // The ending slice id + 1.
PRLock *cxLock_; // Locks cx_ for parallel VM calls PRLock *cxLock_; // Locks cx_ for parallel VM calls
ParallelBailoutRecord *const records_; // Bailout records for each worker ParallelBailoutRecord *const records_; // Bailout records for each worker
@ -377,8 +387,8 @@ class ForkJoinShared : public ParallelJob, public Monitor
ForkJoinShared(JSContext *cx, ForkJoinShared(JSContext *cx,
ThreadPool *threadPool, ThreadPool *threadPool,
HandleFunction fun, HandleFunction fun,
uint16_t sliceFrom, uint16_t sliceStart,
uint16_t sliceTo, uint16_t sliceEnd,
ParallelBailoutRecord *records); ParallelBailoutRecord *records);
~ForkJoinShared(); ~ForkJoinShared();
@ -492,19 +502,24 @@ static const char *ForkJoinModeString(ForkJoinMode mode);
bool bool
js::ForkJoin(JSContext *cx, CallArgs &args) js::ForkJoin(JSContext *cx, CallArgs &args)
{ {
JS_ASSERT(args.length() == 3); // else the self-hosted code is wrong JS_ASSERT(args.length() == 4); // else the self-hosted code is wrong
JS_ASSERT(args[0].isObject()); JS_ASSERT(args[0].isObject());
JS_ASSERT(args[0].toObject().is<JSFunction>()); JS_ASSERT(args[0].toObject().is<JSFunction>());
JS_ASSERT(args[1].isObject()); JS_ASSERT(args[1].isInt32());
JS_ASSERT(args[1].toObject().is<JSFunction>());
JS_ASSERT(args[2].isInt32()); JS_ASSERT(args[2].isInt32());
JS_ASSERT(args[2].toInt32() < NumForkJoinModes); JS_ASSERT(args[3].isInt32());
JS_ASSERT(args[3].toInt32() < NumForkJoinModes);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
RootedFunction boundsFun(cx, &args[1].toObject().as<JSFunction>()); uint16_t sliceStart = (uint16_t)(args[1].toInt32());
ForkJoinMode mode = (ForkJoinMode) args[2].toInt32(); uint16_t sliceEnd = (uint16_t)(args[2].toInt32());
ForkJoinMode mode = (ForkJoinMode)(args[3].toInt32());
ForkJoinOperation op(cx, fun, boundsFun, mode); MOZ_ASSERT(sliceStart == args[1].toInt32());
MOZ_ASSERT(sliceEnd == args[2].toInt32());
MOZ_ASSERT(sliceStart < sliceEnd);
ForkJoinOperation op(cx, fun, sliceStart, sliceEnd, mode);
ExecutionStatus status = op.apply(); ExecutionStatus status = op.apply();
if (status == ExecutionFatal) if (status == ExecutionFatal)
return false; return false;
@ -562,15 +577,16 @@ ForkJoinModeString(ForkJoinMode mode) {
return "???"; return "???";
} }
ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, HandleFunction boundsFun, ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
ForkJoinMode mode) uint16_t sliceEnd, ForkJoinMode mode)
: bailouts(0), : bailouts(0),
bailoutCause(ParallelBailoutNone), bailoutCause(ParallelBailoutNone),
bailoutScript(cx), bailoutScript(cx),
bailoutBytecode(nullptr), bailoutBytecode(nullptr),
cx_(cx), cx_(cx),
fun_(fun), fun_(fun),
boundsFun_(boundsFun), sliceStart_(sliceStart),
sliceEnd_(sliceEnd),
bailoutRecords_(cx), bailoutRecords_(cx),
worklist_(cx), worklist_(cx),
worklistData_(cx), worklistData_(cx),
@ -1005,9 +1021,13 @@ ForkJoinOperation::sequentialExecution(bool disqualified)
Spew(SpewOps, "Executing sequential execution (disqualified=%d).", Spew(SpewOps, "Executing sequential execution (disqualified=%d).",
disqualified); disqualified);
if (sliceStart_ == sliceEnd_)
return ExecutionSequential;
RootedValue funVal(cx_, ObjectValue(*fun_)); RootedValue funVal(cx_, ObjectValue(*fun_));
if (!ExecuteSequentially(cx_, funVal)) if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceEnd_))
return ExecutionFatal; return ExecutionFatal;
MOZ_ASSERT(sliceStart_ == sliceEnd_);
return ExecutionSequential; return ExecutionSequential;
} }
@ -1166,13 +1186,7 @@ ForkJoinOperation::warmupExecution(bool stopIfComplete, ExecutionStatus *status)
// GreenLight: warmup succeeded, still more work to do // GreenLight: warmup succeeded, still more work to do
// RedLight: fatal error or warmup completed all work (check status) // RedLight: fatal error or warmup completed all work (check status)
uint16_t from, to; if (sliceStart_ == sliceEnd_) {
if (!computeBounds(&from, &to)) {
*status = ExecutionFatal;
return RedLight;
}
if (from == to) {
Spew(SpewOps, "Warmup execution finished all the work."); Spew(SpewOps, "Warmup execution finished all the work.");
if (stopIfComplete) { if (stopIfComplete) {
@ -1192,11 +1206,11 @@ ForkJoinOperation::warmupExecution(bool stopIfComplete, ExecutionStatus *status)
return GreenLight; return GreenLight;
} }
Spew(SpewOps, "Executing warmup."); Spew(SpewOps, "Executing warmup from slice %d.", sliceStart_);
AutoEnterWarmup warmup(cx_->runtime()); AutoEnterWarmup warmup(cx_->runtime());
RootedValue funVal(cx_, ObjectValue(*fun_)); RootedValue funVal(cx_, ObjectValue(*fun_));
if (!ExecuteSequentially(cx_, funVal)) { if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceStart_ + 1)) {
*status = ExecutionFatal; *status = ExecutionFatal;
return RedLight; return RedLight;
} }
@ -1215,13 +1229,7 @@ ForkJoinOperation::parallelExecution(ExecutionStatus *status)
// functions such as ForkJoin(). // functions such as ForkJoin().
JS_ASSERT(ForkJoinContext::current() == nullptr); JS_ASSERT(ForkJoinContext::current() == nullptr);
uint16_t from, to; if (sliceStart_ == sliceEnd_) {
if (!computeBounds(&from, &to)) {
*status = ExecutionFatal;
return RedLight;
}
if (from == to) {
Spew(SpewOps, "Warmup execution finished all the work."); Spew(SpewOps, "Warmup execution finished all the work.");
*status = ExecutionWarmup; *status = ExecutionWarmup;
return RedLight; return RedLight;
@ -1229,7 +1237,7 @@ ForkJoinOperation::parallelExecution(ExecutionStatus *status)
ForkJoinActivation activation(cx_); ForkJoinActivation activation(cx_);
ThreadPool *threadPool = &cx_->runtime()->threadPool; ThreadPool *threadPool = &cx_->runtime()->threadPool;
ForkJoinShared shared(cx_, threadPool, fun_, from, to, &bailoutRecords_[0]); ForkJoinShared shared(cx_, threadPool, fun_, sliceStart_, sliceEnd_, &bailoutRecords_[0]);
if (!shared.init()) { if (!shared.init()) {
*status = ExecutionFatal; *status = ExecutionFatal;
return RedLight; return RedLight;
@ -1290,36 +1298,6 @@ ForkJoinOperation::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *sc
return false; return false;
} }
bool
ForkJoinOperation::computeBounds(uint16_t *start, uint16_t *end)
{
RootedValue funVal(cx_, ObjectValue(*boundsFun_));
FastInvokeGuard fig(cx_, funVal);
InvokeArgs &args = fig.args();
if (!args.init(0))
return false;
args.setCallee(funVal);
args.setThis(UndefinedValue());
if (!fig.invoke(cx_))
return false;
MOZ_ASSERT(args.rval().toObject().is<ArrayObject>());
MOZ_ASSERT(args.rval().toObject().getDenseInitializedLength() == 2);
int32_t start32 = args.rval().toObject().getDenseElement(0).toInt32();
int32_t end32 = args.rval().toObject().getDenseElement(1).toInt32();
MOZ_ASSERT(int32_t(uint16_t(start32)) == start32);
MOZ_ASSERT(int32_t(uint16_t(end32)) == end32);
*start = uint16_t(start32);
*end = uint16_t(end32);
return true;
}
// Can only enter callees with a valid IonScript. // Can only enter callees with a valid IonScript.
template <uint32_t maxArgc> template <uint32_t maxArgc>
class ParallelIonInvoke class ParallelIonInvoke
@ -1368,14 +1346,14 @@ class ParallelIonInvoke
ForkJoinShared::ForkJoinShared(JSContext *cx, ForkJoinShared::ForkJoinShared(JSContext *cx,
ThreadPool *threadPool, ThreadPool *threadPool,
HandleFunction fun, HandleFunction fun,
uint16_t sliceFrom, uint16_t sliceStart,
uint16_t sliceTo, uint16_t sliceEnd,
ParallelBailoutRecord *records) ParallelBailoutRecord *records)
: cx_(cx), : cx_(cx),
threadPool_(threadPool), threadPool_(threadPool),
fun_(fun), fun_(fun),
sliceFrom_(sliceFrom), sliceStart_(sliceStart),
sliceTo_(sliceTo), sliceEnd_(sliceEnd),
cxLock_(nullptr), cxLock_(nullptr),
records_(records), records_(records),
allocators_(cx), allocators_(cx),
@ -1445,7 +1423,7 @@ ForkJoinShared::execute()
AutoUnlockMonitor unlock(*this); AutoUnlockMonitor unlock(*this);
// Push parallel tasks and wait until they're all done. // Push parallel tasks and wait until they're all done.
jobResult = threadPool_->executeJob(cx_, this, sliceFrom_, sliceTo_); jobResult = threadPool_->executeJob(cx_, this, sliceStart_, sliceEnd_);
if (jobResult == TP_FATAL) if (jobResult == TP_FATAL)
return TP_FATAL; return TP_FATAL;
} }
@ -1461,7 +1439,7 @@ ForkJoinShared::execute()
#ifdef DEBUG #ifdef DEBUG
Spew(SpewOps, "Completed parallel job [slices: %d, threads: %d, stolen: %d (work stealing:%s)]", Spew(SpewOps, "Completed parallel job [slices: %d, threads: %d, stolen: %d (work stealing:%s)]",
sliceTo_ - sliceFrom_ + 1, sliceEnd_ - sliceStart_,
threadPool_->numWorkers(), threadPool_->numWorkers(),
threadPool_->stolenSlices(), threadPool_->stolenSlices(),
threadPool_->workStealing() ? "ON" : "OFF"); threadPool_->workStealing() ? "ON" : "OFF");
@ -1557,10 +1535,11 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent); cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
setAbortFlagAndRequestInterrupt(false); setAbortFlagAndRequestInterrupt(false);
} else { } else {
ParallelIonInvoke<2> fii(cx_->runtime(), fun_, 2); ParallelIonInvoke<3> fii(cx_->runtime(), fun_, 3);
fii.args[0] = Int32Value(worker->id()); fii.args[0] = Int32Value(worker->id());
fii.args[1] = BooleanValue(false); fii.args[1] = Int32Value(sliceStart_);
fii.args[2] = Int32Value(sliceEnd_);
bool ok = fii.invoke(perThread); bool ok = fii.invoke(perThread);
JS_ASSERT(ok == !cx.bailoutRecord->topScript); JS_ASSERT(ok == !cx.bailoutRecord->topScript);

View File

@ -30,7 +30,7 @@
// to enable parallel execution. At the top-level, it consists of a native // to enable parallel execution. At the top-level, it consists of a native
// function (exposed as the ForkJoin intrinsic) that is used like so: // function (exposed as the ForkJoin intrinsic) that is used like so:
// //
// ForkJoin(func, boundsFunc, mode) // ForkJoin(func, sliceStart, sliceEnd, mode)
// //
// The intention of this statement is to start some some number (usually the // The intention of this statement is to start some some number (usually the
// number of hardware threads) of copies of |func()| running in parallel. Each // number of hardware threads) of copies of |func()| running in parallel. Each
@ -41,38 +41,39 @@
// is not something you should rely upon---if work-stealing is enabled it // is not something you should rely upon---if work-stealing is enabled it
// could be that a single worker thread winds up handling multiple slices. // could be that a single worker thread winds up handling multiple slices.
// //
// The second argument, |boundsFunc|, is a function that must return an array // The second and third arguments, |sliceStart| and |sliceEnd|, are the slice
// of exactly two integers. This function is called before every attempt at // boundaries. These numbers must each fit inside an uint16_t.
// execution: warmup, sequential, or parallel. The bounds are taken from a
// function call instead of taken as two static integers so that the bounds
// may be shrunk when recovering from bailout.
// //
// The third argument, |mode|, is an internal mode integer giving finer // The fourth argument, |mode|, is an internal mode integer giving finer
// control over the behavior of ForkJoin. See the |ForkJoinMode| enum. // control over the behavior of ForkJoin. See the |ForkJoinMode| enum.
// //
// func() should expect the following arguments: // func() should expect the following arguments:
// //
// func(warmup) // func(workerId, sliceStart, sliceEnd)
// //
// The parameter |warmup| is true for a *warmup or recovery phase*. Warmup // The |workerId| parameter is the id of the worker executing the function. It
// phases are discussed below in more detail, but the general idea is that if // is 0 in sequential mode.
// |warmup| is true, |func| should only do a fixed amount of work. If |warmup| //
// is false, |func| should try to do all remaining work is assigned. // The |sliceStart| and |sliceEnd| parameters are the current bounds that that
// the worker is handling. In parallel execution, these parameters are not
// used. In sequential execution, they tell the worker what slices should be
// processed. During the warm up phase, sliceEnd == sliceStart + 1.
// //
// |func| can keep asking for more work from the scheduler by calling the // |func| can keep asking for more work from the scheduler by calling the
// intrinsic |GetForkJoinSlice(id)|. When there are no more slices to hand // intrinsic |GetForkJoinSlice(sliceStart, sliceEnd, id)|. When there are no
// out, -1 is returned as a sentinel value. By exposing this function as an // more slices to hand out, ThreadPool::MAX_SLICE_ID is returned as a sentinel
// intrinsic, we reduce the number of JS-C++ boundary crossings incurred by // value. By exposing this function as an intrinsic, we reduce the number of
// workstealing, which may have many slices. // JS-C++ boundary crossings incurred by workstealing, which may have many
// slices.
// //
// |func| MUST PROCESS ALL SLICES BEFORE RETURNING! Not doing so is an error // In sequential execution, |func| should return the maximum computed slice id
// |and is protected by debug asserts in ThreadPool. // S for which all slices with id < S have already been processed. This is so
// ThreadPool can track the leftmost completed slice id to maintain
// determinism. Slices which have been completed in sequential execution
// cannot be re-run in parallel execution.
// //
// Note well that there is a separation of concern between *scheduling* slices // In parallel execution, |func| MUST PROCESS ALL SLICES BEFORE RETURNING!
// and *interpreting* slices. ForkJoin only schedules slices by handing out // Not doing so is an error and is protected by debug asserts in ThreadPool.
// slice ids; it does not interpret what slice ids mean. Instead, |func|
// should track how much work it has accomplished thus far; consult |Array.js|
// for some examples.
// //
// Warmups and Sequential Fallbacks // Warmups and Sequential Fallbacks
// -------------------------------- // --------------------------------
@ -192,7 +193,7 @@
// during parallel exeution. But we're not there yet. // during parallel exeution. But we're not there yet.
// //
// Load balancing (work stealing): // Load balancing (work stealing):
//
// The ForkJoin job is dynamically divided into a fixed number of slices, // The ForkJoin job is dynamically divided into a fixed number of slices,
// and is submitted for parallel execution in the pool. When the number // and is submitted for parallel execution in the pool. When the number
// of slices is big enough (typically greater than the number of workers // of slices is big enough (typically greater than the number of workers

View File

@ -335,7 +335,7 @@ intrinsic_ForkJoinGetSlicePar(ForkJoinContext *cx, unsigned argc, Value *vp)
if (cx->getSlice(&sliceId)) if (cx->getSlice(&sliceId))
args.rval().setInt32(sliceId); args.rval().setInt32(sliceId);
else else
args.rval().setInt32(-1); args.rval().setInt32(ThreadPool::MAX_SLICE_ID);
return true; return true;
} }

View File

@ -99,8 +99,8 @@ ThreadPoolWorker::discardSlices()
bool bool
ThreadPoolWorker::stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId) ThreadPoolWorker::stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId)
{ {
// Instead of popping the slice from the front by incrementing sliceFrom_, // Instead of popping the slice from the front by incrementing sliceStart_,
// decrement sliceTo_. Usually this gives us better locality. // decrement sliceEnd_. Usually this gives us better locality.
if (!victim->popSliceBack(sliceId)) if (!victim->popSliceBack(sliceId))
return false; return false;
#ifdef DEBUG #ifdef DEBUG
@ -198,10 +198,10 @@ ThreadPoolWorker::helperLoop()
} }
void void
ThreadPoolWorker::submitSlices(uint16_t sliceFrom, uint16_t sliceTo) ThreadPoolWorker::submitSlices(uint16_t sliceStart, uint16_t sliceEnd)
{ {
MOZ_ASSERT(!hasWork()); MOZ_ASSERT(!hasWork());
sliceBounds_ = ComposeSliceBounds(sliceFrom, sliceTo); sliceBounds_ = ComposeSliceBounds(sliceStart, sliceEnd);
} }
bool bool
@ -392,9 +392,9 @@ ThreadPool::waitForWorkers(AutoLockMonitor &lock)
} }
ParallelResult ParallelResult
ThreadPool::executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceFrom, uint16_t sliceMax) ThreadPool::executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceStart, uint16_t sliceMax)
{ {
MOZ_ASSERT(sliceFrom < sliceMax); MOZ_ASSERT(sliceStart < sliceMax);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
MOZ_ASSERT(activeWorkers_ == 0); MOZ_ASSERT(activeWorkers_ == 0);
MOZ_ASSERT(!hasWork()); MOZ_ASSERT(!hasWork());
@ -403,19 +403,19 @@ ThreadPool::executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceFrom, uint
return TP_FATAL; return TP_FATAL;
// Evenly distribute slices to the workers. // Evenly distribute slices to the workers.
uint16_t numSlices = sliceMax - sliceFrom; uint16_t numSlices = sliceMax - sliceStart;
uint16_t slicesPerWorker = numSlices / numWorkers(); uint16_t slicesPerWorker = numSlices / numWorkers();
uint16_t leftover = numSlices % numWorkers(); uint16_t leftover = numSlices % numWorkers();
uint16_t sliceTo = sliceFrom; uint16_t sliceEnd = sliceStart;
for (uint32_t workerId = 0; workerId < numWorkers(); workerId++) { for (uint32_t workerId = 0; workerId < numWorkers(); workerId++) {
if (leftover > 0) { if (leftover > 0) {
sliceTo += slicesPerWorker + 1; sliceEnd += slicesPerWorker + 1;
leftover--; leftover--;
} else { } else {
sliceTo += slicesPerWorker; sliceEnd += slicesPerWorker;
} }
workers_[workerId]->submitSlices(sliceFrom, sliceTo); workers_[workerId]->submitSlices(sliceStart, sliceEnd);
sliceFrom = sliceTo; sliceStart = sliceEnd;
} }
MOZ_ASSERT(leftover == 0); MOZ_ASSERT(leftover == 0);

View File

@ -81,7 +81,7 @@ class ThreadPoolWorker
bool isMainThread() const { return id() == 0; } bool isMainThread() const { return id() == 0; }
// Submits a new set of slices. Assumes !hasWork(). // Submits a new set of slices. Assumes !hasWork().
void submitSlices(uint16_t sliceFrom, uint16_t sliceTo); void submitSlices(uint16_t sliceStart, uint16_t sliceEnd);
// Get the next slice; work stealing happens here if work stealing is // Get the next slice; work stealing happens here if work stealing is
// on. Returns false if there are no more slices to hand out. // on. Returns false if there are no more slices to hand out.
@ -208,6 +208,8 @@ class ThreadPool : public Monitor
return offsetof(ThreadPool, workers_); return offsetof(ThreadPool, workers_);
} }
static const uint16_t MAX_SLICE_ID = UINT16_MAX;
ThreadPool(JSRuntime *rt); ThreadPool(JSRuntime *rt);
~ThreadPool(); ~ThreadPool();