mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 972581 part 2 -- Add 1-dim mapPar r=shu
This commit is contained in:
parent
3457a4acff
commit
fbc51eec04
@ -377,6 +377,7 @@ selfhosting_srcs := \
|
||||
$(srcdir)/builtin/Iterator.js \
|
||||
$(srcdir)/builtin/Map.js \
|
||||
$(srcdir)/builtin/Number.js \
|
||||
$(srcdir)/builtin/Parallel.js \
|
||||
$(srcdir)/builtin/String.js \
|
||||
$(srcdir)/builtin/Set.js \
|
||||
$(srcdir)/builtin/TypedObject.js \
|
||||
|
@ -570,116 +570,6 @@ function ArrayKeys() {
|
||||
* http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism
|
||||
*/
|
||||
|
||||
/* 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)
|
||||
|
||||
/* Safe versions of ARRAY.push(ELEMENT) */
|
||||
#define ARRAY_PUSH(ARRAY, ELEMENT) \
|
||||
callFunction(std_Array_push, ARRAY, ELEMENT);
|
||||
#define ARRAY_SLICE(ARRAY, ELEMENT) \
|
||||
callFunction(std_Array_slice, ARRAY, ELEMENT);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* 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)]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array by applying |func(e, i, self)| for each element |e|
|
||||
* with index |i|.
|
||||
|
67
js/src/builtin/Parallel.js
Normal file
67
js/src/builtin/Parallel.js
Normal file
@ -0,0 +1,67 @@
|
||||
/* 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)]
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1426,6 +1426,8 @@ TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
|
||||
void
|
||||
TypedObject::attach(TypedObject &typedObj, int32_t offset)
|
||||
{
|
||||
JS_ASSERT(typedObj.typedMem() != NULL);
|
||||
|
||||
attach(typedObj.owner(), typedObj.offset() + offset);
|
||||
}
|
||||
|
||||
@ -1453,8 +1455,9 @@ TypedObjLengthFromType(TypeDescr &descr)
|
||||
|
||||
/*static*/ TypedObject *
|
||||
TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
HandleTypedObject typedObj, size_t offset)
|
||||
HandleTypedObject typedObj, size_t offset)
|
||||
{
|
||||
JS_ASSERT(typedObj->typedMem() != NULL);
|
||||
JS_ASSERT(offset <= typedObj->size());
|
||||
JS_ASSERT(offset + type->size() <= typedObj->size());
|
||||
|
||||
@ -2665,6 +2668,28 @@ JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo,
|
||||
AttachTypedObjectJitInfo,
|
||||
js::AttachTypedObject);
|
||||
|
||||
bool
|
||||
js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(argc == 2);
|
||||
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[1].isInt32());
|
||||
|
||||
TypedObject &typedObj = args[0].toObject().as<TypedObject>();
|
||||
int32_t offset = args[1].toInt32();
|
||||
|
||||
JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already
|
||||
|
||||
typedObj.setPrivate(typedObj.owner().dataPointer() + offset);
|
||||
typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::SetTypedObjectOffsetJitInfo,
|
||||
SetTypedObjectJitInfo,
|
||||
js::SetTypedObjectOffset);
|
||||
|
||||
bool
|
||||
js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -696,6 +696,15 @@ bool NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool AttachTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
|
||||
extern const JSJitInfo AttachTypedObjectJitInfo;
|
||||
|
||||
/*
|
||||
* Usage: SetTypedObjectOffset(typedObj, offset)
|
||||
*
|
||||
* Changes the offset for `typedObj` within its buffer to `offset`.
|
||||
* `typedObj` must already be attached.
|
||||
*/
|
||||
bool SetTypedObjectOffset(ThreadSafeContext *cx, unsigned argc, Value *vp);
|
||||
extern const JSJitInfo SetTypedObjectOffsetJitInfo;
|
||||
|
||||
/*
|
||||
* Usage: ObjectIsTypeDescr(obj)
|
||||
*
|
||||
|
@ -447,6 +447,11 @@ TypedObjectPointer.prototype.getX4 = function() {
|
||||
//
|
||||
// The methods in this section modify the data pointed at by `this`.
|
||||
|
||||
// Convenience function
|
||||
function SetTypedObjectValue(descr, typedObj, offset, fromValue) {
|
||||
new TypedObjectPointer(descr, typedObj, offset).set(fromValue);
|
||||
}
|
||||
|
||||
// Assigns `fromValue` to the memory pointed at by `this`, adapting it
|
||||
// to `typeRepr` as needed. This is the most general entry point and
|
||||
// works for any type.
|
||||
@ -934,10 +939,23 @@ function TypedArrayMap(a, b) {
|
||||
return MapTypedSeqImpl(this, a, thisType, b);
|
||||
else if (typeof a === "function")
|
||||
return MapTypedSeqImpl(this, 1, thisType, a);
|
||||
else if (typeof a === "number")
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
}
|
||||
|
||||
// Warning: user exposed!
|
||||
function TypedArrayMapPar(a, b) {
|
||||
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
else
|
||||
var thisType = TYPEDOBJ_TYPE_DESCR(this);
|
||||
if (!TypeDescrIsArrayType(thisType))
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
|
||||
// Arguments: [depth], func
|
||||
if (typeof a === "number" && typeof b === "function")
|
||||
return MapTypedParImpl(this, a, thisType, b);
|
||||
else if (typeof a === "function")
|
||||
return MapTypedParImpl(this, 1, thisType, a);
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
}
|
||||
|
||||
// Warning: user exposed!
|
||||
@ -1001,11 +1019,6 @@ function TypedObjectArrayTypeFromPar(a,b,c) {
|
||||
return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
|
||||
}
|
||||
|
||||
// Warning: user exposed!
|
||||
function TypedArrayMapPar(a, b) {
|
||||
return callFunction(TypedArrayMap, this, a, b);
|
||||
}
|
||||
|
||||
// Warning: user exposed!
|
||||
function TypedArrayReducePar(a, b) {
|
||||
return callFunction(TypedArrayReduce, this, a, b);
|
||||
@ -1036,6 +1049,12 @@ function GET_BIT(data, index) {
|
||||
return (data[word] & mask) != 0;
|
||||
}
|
||||
|
||||
function TypeDescrIsUnsizedArrayType(t) {
|
||||
assert(IsObject(t) && ObjectIsTypeDescr(t),
|
||||
"TypeDescrIsArrayType called on non-type-object");
|
||||
return DESCR_KIND(t) === JS_TYPEREPR_UNSIZED_ARRAY_KIND;
|
||||
}
|
||||
|
||||
function TypeDescrIsArrayType(t) {
|
||||
assert(IsObject(t) && ObjectIsTypeDescr(t), "TypeDescrIsArrayType called on non-type-object");
|
||||
|
||||
@ -1073,7 +1092,8 @@ function TypeDescrIsSizedArrayType(t) {
|
||||
}
|
||||
|
||||
function TypeDescrIsSimpleType(t) {
|
||||
assert(IsObject(t) && ObjectIsTypeDescr(t), "TypeDescrIsSimpleType called on non-type-object");
|
||||
assert(IsObject(t) && ObjectIsTypeDescr(t),
|
||||
"TypeDescrIsSimpleType called on non-type-object");
|
||||
|
||||
var kind = DESCR_KIND(t);
|
||||
switch (kind) {
|
||||
@ -1326,6 +1346,183 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) {
|
||||
|
||||
}
|
||||
|
||||
// Implements |map| and |from| methods for typed |inArray|.
|
||||
function MapTypedParImpl(inArray, depth, outputType, func) {
|
||||
assert(IsObject(outputType) && ObjectIsTypeDescr(outputType),
|
||||
"Map/From called on non-type-object outputType");
|
||||
assert(IsObject(inArray) && ObjectIsTypedObject(inArray),
|
||||
"Map/From called on non-object or untyped input array.");
|
||||
assert(TypeDescrIsArrayType(outputType),
|
||||
"Map/From called on non array-type outputType");
|
||||
|
||||
var inArrayType = TypeOfTypedObject(inArray);
|
||||
|
||||
if (ShouldForceSequential() ||
|
||||
depth <= 0 ||
|
||||
TO_INT32(depth) !== depth ||
|
||||
!TypeDescrIsArrayType(inArrayType) ||
|
||||
!TypeDescrIsUnsizedArrayType(outputType))
|
||||
{
|
||||
// defer error cases to seq implementation:
|
||||
return MapTypedSeqImpl(inArray, depth, outputType, func);
|
||||
}
|
||||
|
||||
switch (depth) {
|
||||
case 1:
|
||||
return MapTypedParImplDepth1(inArray, inArrayType, outputType, func);
|
||||
default:
|
||||
return MapTypedSeqImpl(inArray, depth, outputType, func);
|
||||
}
|
||||
}
|
||||
|
||||
function RedirectPointer(typedObj, offset, outputIsScalar) {
|
||||
if (!outputIsScalar || !InParallelSection()) {
|
||||
// ^ Subtle note: always check InParallelSection() last, because
|
||||
// otherwise the other if conditions will not execute during
|
||||
// sequential mode and we will not gather enough type
|
||||
// information.
|
||||
|
||||
// Here `typedObj` represents the input or output pointer we will
|
||||
// pass to the user function. Ideally, we will just update the
|
||||
// offset of `typedObj` in place so that it moves along the
|
||||
// input/output buffer without incurring any allocation costs. But
|
||||
// we can only do this if these changes are invisible to the user.
|
||||
//
|
||||
// Under normal uses, such changes *should* be invisible -- the
|
||||
// in/out pointers are only intended to be used during the
|
||||
// callback and then discarded, but of course in the general case
|
||||
// nothing prevents them from escaping.
|
||||
//
|
||||
// However, if we are in parallel mode, we know that the pointers
|
||||
// will not escape into global state. They could still escape by
|
||||
// being returned into the resulting array, but even that avenue
|
||||
// is impossible if the result array cannot contain objects.
|
||||
//
|
||||
// Therefore, we reuse a pointer if we are both in parallel mode
|
||||
// and we have a transparent output type. It'd be nice to loosen
|
||||
// this condition later by using fancy ion optimizations that
|
||||
// assume the value won't escape and copy it if it does. But those
|
||||
// don't exist yet. Moreover, checking if the type is transparent
|
||||
// is an overapproximation: users can manually declare opaque
|
||||
// types that nonetheless only contain scalar data.
|
||||
|
||||
typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj),
|
||||
typedObj, 0);
|
||||
}
|
||||
|
||||
SetTypedObjectOffset(typedObj, offset);
|
||||
return typedObj;
|
||||
}
|
||||
SetScriptHints(RedirectPointer, { inline: true });
|
||||
|
||||
function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
|
||||
assert(IsObject(inArrayType) && ObjectIsTypeDescr(inArrayType) &&
|
||||
TypeDescrIsArrayType(inArrayType),
|
||||
"DoMapTypedParDepth1: invalid inArrayType");
|
||||
assert(IsObject(outArrayType) && ObjectIsTypeDescr(outArrayType) &&
|
||||
TypeDescrIsArrayType(outArrayType),
|
||||
"DoMapTypedParDepth1: invalid outArrayType");
|
||||
assert(IsObject(inArray) && ObjectIsTypedObject(inArray),
|
||||
"DoMapTypedParDepth1: invalid inArray");
|
||||
|
||||
// Determine the grain types of the input and output.
|
||||
const inGrainType = inArrayType.elementType;
|
||||
const outGrainType = outArrayType.elementType;
|
||||
const inGrainTypeSize = DESCR_SIZE(inGrainType);
|
||||
const outGrainTypeSize = DESCR_SIZE(outGrainType);
|
||||
const inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType);
|
||||
const outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
|
||||
|
||||
const length = inArray.length;
|
||||
const mode = undefined;
|
||||
|
||||
const outArray = new outArrayType(length);
|
||||
|
||||
const outGrainTypeIsTransparent = ObjectIsTransparentTypedObject(outArray);
|
||||
|
||||
// Construct the slices and initial pointers for each worker:
|
||||
const slicesInfo = ComputeSlicesInfo(length);
|
||||
const numWorkers = ForkJoinNumWorkers();
|
||||
assert(numWorkers > 0, "Should have at least the main thread");
|
||||
const pointers = [];
|
||||
for (var i = 0; i < numWorkers; i++) {
|
||||
const inPointer = new TypedObjectPointer(inGrainType, inArray, 0);
|
||||
const inTypedObject = inPointer.getDerivedIf(inGrainTypeIsComplex);
|
||||
const outPointer = new TypedObjectPointer(outGrainType, outArray, 0);
|
||||
const outTypedObject = outPointer.getOpaqueIf(outGrainTypeIsComplex);
|
||||
ARRAY_PUSH(pointers, ({ inTypedObject: inTypedObject,
|
||||
outTypedObject: outTypedObject }));
|
||||
}
|
||||
|
||||
// Below we will be adjusting offsets within the input to point at
|
||||
// successive entries; we'll need to know the offset of inArray
|
||||
// relative to its owner (which is often but not always 0).
|
||||
const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray);
|
||||
|
||||
ForkJoin(mapThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
|
||||
return outArray;
|
||||
|
||||
function mapThread(workerId, warmup) {
|
||||
assert(TO_INT32(workerId) === workerId,
|
||||
"workerId not int: " + workerId);
|
||||
assert(workerId >= 0 && workerId < pointers.length,
|
||||
"workerId too large: " + workerId + " >= " + pointers.length);
|
||||
assert(!!pointers[workerId],
|
||||
"no pointer data for workerId: " + workerId);
|
||||
|
||||
var sliceId;
|
||||
const { inTypedObject, outTypedObject } = pointers[workerId];
|
||||
|
||||
while (GET_SLICE(slicesInfo, sliceId)) {
|
||||
const indexStart = SLICE_START(slicesInfo, sliceId);
|
||||
const indexEnd = SLICE_END(slicesInfo, indexStart, length);
|
||||
|
||||
var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart);
|
||||
var outOffset = std_Math_imul(outGrainTypeSize, indexStart);
|
||||
|
||||
// Set the target region so that user is only permitted to write
|
||||
// within the range set aside for this slice. This prevents user
|
||||
// from writing to typed objects that escaped from prior slices
|
||||
// during sequential iteration. Note that, for any particular
|
||||
// iteration of the loop below, it's only valid to write to the
|
||||
// memory range corresponding to the index `i` -- however, since
|
||||
// the different iterations cannot communicate typed object
|
||||
// pointers to one another during parallel exec, we need only
|
||||
// fear escaped typed objects from *other* slices, so we can
|
||||
// just set the target region once.
|
||||
const endOffset = std_Math_imul(outGrainTypeSize, indexEnd);
|
||||
SetForkJoinTargetRegion(outArray, outOffset, endOffset);
|
||||
|
||||
for (var i = indexStart; i < indexEnd; i++) {
|
||||
var inVal = (inGrainTypeIsComplex
|
||||
? RedirectPointer(inTypedObject, inOffset,
|
||||
outGrainTypeIsTransparent)
|
||||
: inArray[i]);
|
||||
var outVal = (outGrainTypeIsComplex
|
||||
? RedirectPointer(outTypedObject, outOffset,
|
||||
outGrainTypeIsTransparent)
|
||||
: undefined);
|
||||
const r = func(inVal, i, inArray, outVal);
|
||||
if (r !== undefined) {
|
||||
if (outGrainTypeIsComplex)
|
||||
SetTypedObjectValue(outGrainType, outArray, outOffset, r);
|
||||
else
|
||||
outArray[i] = r;
|
||||
}
|
||||
inOffset += inGrainTypeSize;
|
||||
outOffset += outGrainTypeSize;
|
||||
}
|
||||
|
||||
MARK_SLICE_DONE(slicesInfo, sliceId);
|
||||
if (warmup)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
SetScriptHints(MapTypedParImplDepth1, { cloneAtCallsite: true });
|
||||
|
||||
function ReduceTypedSeqImpl(array, outputType, func, initial) {
|
||||
assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array.");
|
||||
assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType");
|
||||
|
@ -90,6 +90,64 @@ var std_Set_iterator = Set.prototype[std_iterator];
|
||||
var std_Map_iterator_next = Object.getPrototypeOf(Map()[std_iterator]()).next;
|
||||
var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
|
||||
|
||||
/* Safe versions of ARRAY.push(ELEMENT) */
|
||||
#define ARRAY_PUSH(ARRAY, ELEMENT) \
|
||||
callFunction(std_Array_push, ARRAY, ELEMENT);
|
||||
#define 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 **********/
|
||||
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
// Test basic mapPar parallel execution using an out
|
||||
// pointer to generate a struct return.
|
||||
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
load(libdir + "parallelarray-helpers.js")
|
||||
|
||||
var { ArrayType, StructType, uint32 } = TypedObject;
|
||||
|
||||
function test() {
|
||||
var L = minItemsTestingThreshold;
|
||||
var Point = new StructType({x: uint32, y: uint32});
|
||||
var Points = Point.array();
|
||||
var points = new Points(L);
|
||||
for (var i = 0; i < L; i++)
|
||||
points[i].x = i;
|
||||
|
||||
assertParallelExecSucceeds(
|
||||
function() points.mapPar(function(p, i, c, out) { out.y = p.x; }),
|
||||
function(points2) {
|
||||
for (var i = 0; i < L; i++) {
|
||||
assertEq(points[i].x, i);
|
||||
assertEq(points[i].y, 0);
|
||||
assertEq(points2[i].x, 0);
|
||||
assertEq(points2[i].y, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -0,0 +1,23 @@
|
||||
// Test basic mapPar parallel execution.
|
||||
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
load(libdir + "parallelarray-helpers.js")
|
||||
|
||||
var { ArrayType, StructType, uint32 } = TypedObject;
|
||||
|
||||
function test() {
|
||||
var L = minItemsTestingThreshold;
|
||||
var Uints = uint32.array(L);
|
||||
var uints1 = new Uints();
|
||||
assertParallelExecSucceeds(
|
||||
function() uints1.mapPar(function(e) e + 1),
|
||||
function(uints2) {
|
||||
for (var i = 0; i < L; i++)
|
||||
assertEq(uints1[i] + 1, uints2[i]);
|
||||
});
|
||||
}
|
||||
|
||||
test();
|
||||
|
@ -692,6 +692,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FNINFO("AttachTypedObject",
|
||||
JSNativeThreadSafeWrapper<js::AttachTypedObject>,
|
||||
&js::AttachTypedObjectJitInfo, 5, 0),
|
||||
JS_FNINFO("SetTypedObjectOffset",
|
||||
JSNativeThreadSafeWrapper<js::SetTypedObjectOffset>,
|
||||
&js::SetTypedObjectOffsetJitInfo, 2, 0),
|
||||
JS_FNINFO("ObjectIsTypeDescr",
|
||||
JSNativeThreadSafeWrapper<js::ObjectIsTypeDescr>,
|
||||
&js::ObjectIsTypeDescrJitInfo, 5, 0),
|
||||
|
Loading…
Reference in New Issue
Block a user