Bug 778559 - Implement ParallelArray API with sequential execution (r=dmandelin)

This commit is contained in:
Shu-yu Guo 2012-08-16 14:03:44 -07:00
parent 7c02696c5e
commit fe6e21ff38
69 changed files with 3307 additions and 0 deletions

View File

@ -120,6 +120,7 @@ CPPSRCS = \
BytecodeCompiler.cpp \
BytecodeEmitter.cpp \
FoldConstants.cpp \
ParallelArray.cpp \
ParseMaps.cpp \
ParseNode.cpp \
Parser.cpp \

View File

@ -0,0 +1,195 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
*
* 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/. */
#ifndef ParallelArray_inl_h__
#define ParallelArray_inl_h__
#include "builtin/ParallelArray.h"
#include "jsobjinlines.h"
namespace js {
inline uint32_t
ParallelArrayObject::IndexInfo::scalarLengthOfDimensions()
{
JS_ASSERT(isInitialized());
return dimensions[0] * partialProducts[0];
}
inline uint32_t
ParallelArrayObject::IndexInfo::toScalar()
{
JS_ASSERT(isInitialized());
JS_ASSERT(indices.length() <= partialProducts.length());
if (indices.length() == 0)
return 0;
if (dimensions.length() == 1)
return indices[0];
uint32_t index = indices[0] * partialProducts[0];
for (uint32_t i = 1; i < indices.length(); i++)
index += indices[i] * partialProducts[i];
return index;
}
inline bool
ParallelArrayObject::IndexInfo::fromScalar(uint32_t index)
{
JS_ASSERT(isInitialized());
if (!indices.resize(partialProducts.length()))
return false;
if (dimensions.length() == 1) {
indices[0] = index;
return true;
}
uint32_t prev = index;
uint32_t d;
for (d = 0; d < partialProducts.length() - 1; d++) {
indices[d] = prev / partialProducts[d];
prev = prev % partialProducts[d];
}
indices[d] = prev;
return true;
}
inline bool
ParallelArrayObject::IndexInfo::initialize(uint32_t space)
{
// Initialize using a manually set dimension vector.
JS_ASSERT(dimensions.length() > 0);
JS_ASSERT(space <= dimensions.length());
// Compute the partial products of the dimensions.
//
// NB: partialProducts[i] is the scalar length of dimension i. The scalar
// length of the entire space is thus dimensions[0] * partialProducts[0].
uint32_t ndims = dimensions.length();
if (!partialProducts.resize(ndims))
return false;
partialProducts[ndims - 1] = 1;
for (uint32_t i = ndims - 1; i > 0; i--)
partialProducts[i - 1] = dimensions[i] * partialProducts[i];
// Reserve indices.
return indices.reserve(ndims) && indices.resize(space);
}
inline bool
ParallelArrayObject::IndexInfo::initialize(JSContext *cx, HandleParallelArrayObject source,
uint32_t space)
{
// Initialize using a dimension vector gotten from a parallel array
// source.
if (!source->getDimensions(cx, dimensions))
return false;
return initialize(space);
}
inline bool
ParallelArrayObject::DenseArrayToIndexVector(JSContext *cx, HandleObject obj,
IndexVector &indices)
{
uint32_t length = obj->getDenseArrayInitializedLength();
if (!indices.resize(length))
return false;
// Read the index vector out of the dense array into an actual Vector for
// ease of access. We're guaranteed that the elements of the dense array
// are uint32s, so just cast.
const Value *src = obj->getDenseArrayElements();
const Value *end = src + length;
for (uint32_t *dst = indices.begin(); src < end; dst++, src++)
*dst = static_cast<uint32_t>(src->toInt32());
return true;
}
inline bool
ParallelArrayObject::is(const Value &v)
{
return v.isObject() && is(&v.toObject());
}
inline bool
ParallelArrayObject::is(JSObject *obj)
{
return obj->hasClass(&class_);
}
inline ParallelArrayObject *
ParallelArrayObject::as(JSObject *obj)
{
JS_ASSERT(is(obj));
return static_cast<ParallelArrayObject *>(obj);
}
inline JSObject *
ParallelArrayObject::dimensionArray()
{
JSObject &dimObj = getSlot(SLOT_DIMENSIONS).toObject();
JS_ASSERT(dimObj.isDenseArray());
return &dimObj;
}
inline JSObject *
ParallelArrayObject::buffer()
{
JSObject &buf = getSlot(SLOT_BUFFER).toObject();
JS_ASSERT(buf.isDenseArray());
return &buf;
}
inline uint32_t
ParallelArrayObject::bufferOffset()
{
return static_cast<uint32_t>(getSlot(SLOT_BUFFER_OFFSET).toInt32());
}
inline uint32_t
ParallelArrayObject::outermostDimension()
{
return static_cast<uint32_t>(dimensionArray()->getDenseArrayElement(0).toInt32());
}
inline bool
ParallelArrayObject::isOneDimensional()
{
return dimensionArray()->getDenseArrayInitializedLength() == 1;
}
inline bool
ParallelArrayObject::inOutermostDimensionRange(uint32_t index)
{
return index < outermostDimension();
}
inline bool
ParallelArrayObject::inOutermostDimensionRange(JSContext *cx, HandleId id)
{
uint32_t i;
return js_IdIsIndex(id, &i) && inOutermostDimensionRange(i);
}
inline bool
ParallelArrayObject::getDimensions(JSContext *cx, IndexVector &dims)
{
RootedObject obj(cx, dimensionArray());
if (!obj)
return false;
return DenseArrayToIndexVector(cx, obj, dims);
}
} // namespace js
#endif // ParallelArray_inl_h__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,414 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
*
* 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/. */
#ifndef ParallelArray_h__
#define ParallelArray_h__
#include "jsapi.h"
#include "jscntxt.h"
#include "jsobj.h"
namespace js {
class ParallelArrayObject;
typedef Rooted<ParallelArrayObject *> RootedParallelArrayObject;
typedef Handle<ParallelArrayObject *> HandleParallelArrayObject;
//
// ParallelArray Overview
//
// Parallel arrays are immutable, possibly multi-dimensional arrays which
// enable parallel computation based on a few base operations. The execution
// model is one of fallback: try to execute operations in parallel, falling
// back to sequential implementation if (for whatever reason) the operation
// could not be executed in paralle. The API allows leeway to implementers to
// decide both representation and what is considered parallelizable.
//
// Currently ParallelArray objects are backed by dense arrays for ease of
// GC. For the higher-dimensional case, data is stored in a packed, row-major
// order representation in the backing dense array. See notes below about
// IndexInfo in how to convert between scalar offsets into the backing array
// and a vector of indices.
//
// Except for the comprehension form, all operations (e.g. map, filter,
// reduce) operate on the outermost dimension only. That is, those operations
// only operate on the "rows" of the array. "Element" is used in context of
// ParallelArray objects to mean any indexable value of a ParallelArray
// object. For a one dimensional array, elements are always scalar values. For
// a higher dimensional array, elements could either be scalar values
// (i.e. leaves) or ParallelArray objects of lesser dimensions
// (i.e. subarrays).
//
class ParallelArrayObject : public JSObject {
public:
typedef Vector<uint32_t, 4> IndexVector;
//
// Helper structure to help index higher-dimensional arrays to convert
// between a vector of indices and scalar offsets for use in the flat
// backing dense array.
//
// IndexInfo instances _must_ be initialized using one of the initialize
// methods before use.
//
// Typical usage is stack allocating an IndexInfo, initializing it with a
// particular source ParallelArray object's dimensionality, and mutating
// the indices member vector. For instance, to iterate through everything
// in the first 2 dimensions of an array of > 2 dimensions:
//
// IndexInfo iv(cx);
// if (!iv.initialize(cx, source, 2))
// return false;
// for (uint32_t i = 0; i < iv.dimensions[0]; i++) {
// for (uint32_t j = 0; j < iv.dimensions[1]; j++) {
// iv.indices[0] = i;
// iv.indices[1] = j;
// if (source->getParallelArrayElement(cx, iv, &elem))
// ...
// }
// }
//
// Note from the above that it is not required to fill out the indices
// vector up to the full dimensionality. For an N-dimensional array,
// having an indices vector of length D < N indexes a subarray.
//
struct IndexInfo {
// Vector of indices. Should be large enough to hold up to
// dimensions.length indices.
IndexVector indices;
// Vector of dimensions of the ParallelArray object that the indices
// are meant to index into.
IndexVector dimensions;
// Cached partial products of the dimensions defined by the following
// recurrence:
//
// partialProducts[n] =
// 1 if n == |dimensions|
// dimensions[n+1] * partialProducts[n+1] otherwise
//
// These are used for computing scalar offsets.
IndexVector partialProducts;
IndexInfo(JSContext *cx)
: indices(cx), dimensions(cx), partialProducts(cx)
{}
// Prepares indices and computes partial products. The space argument
// is the index space. The indices vector is resized to be of length
// space.
//
// The dimensions vector must be filled already, and space must be <=
// dimensions.length().
inline bool initialize(uint32_t space);
// Load dimensions from a source, then initialize as above.
inline bool initialize(JSContext *cx, HandleParallelArrayObject source,
uint32_t space);
// Get the scalar length according to the dimensions vector, i.e. the
// product of the dimensions vector.
inline uint32_t scalarLengthOfDimensions();
// Compute the scalar index from the current index vector.
inline uint32_t toScalar();
// Set the index vector according to a scalar index.
inline bool fromScalar(uint32_t index);
bool isInitialized();
};
static JSObject *initClass(JSContext *cx, JSObject *obj);
static Class class_;
static inline bool is(const Value &v);
static inline bool is(JSObject *obj);
static inline ParallelArrayObject *as(JSObject *obj);
inline JSObject *dimensionArray();
inline JSObject *buffer();
inline uint32_t bufferOffset();
inline uint32_t outermostDimension();
inline bool isOneDimensional();
inline bool inOutermostDimensionRange(uint32_t index);
inline bool inOutermostDimensionRange(JSContext *cx, HandleId id);
inline bool getDimensions(JSContext *cx, IndexVector &dims);
// Specialized for one dimensional arrays. Use this if possible.
bool getElementFromOnlyDimension(JSContext *cx, uint32_t index, MutableHandleValue vp);
// The general case; works for arrays of any dimensionality.
bool getParallelArrayElement(JSContext *cx, IndexInfo &iv, MutableHandleValue vp);
// Convenience function for getting an element from the outermost
// dimension in the general case. This creates a temporary IndexInfo of
// length 1 with the 1st index set to the index parameter.
bool getParallelArrayElement(JSContext *cx, uint32_t index, MutableHandleValue vp);
bool toStringBuffer(JSContext *cx, bool useLocale, StringBuffer &sb);
private:
enum {
// The ParallelArray API refers to dimensions as "shape", but to avoid
// confusion with the internal engine notion of a shape we call it
// "dimensions" here.
SLOT_DIMENSIONS = 0,
// Underlying dense array.
SLOT_BUFFER,
// First index of the underlying buffer to be considered in bounds.
SLOT_BUFFER_OFFSET,
RESERVED_SLOTS
};
enum ExecutionStatus {
ExecutionFailed = 0,
ExecutionCompiled,
ExecutionSucceeded
};
// Execution modes are kept as static instances of structs that implement
// a signature that comprises of build, map, fold, scatter, and filter,
// whose argument lists are defined in the macros below.
//
// Even though the base class |ExecutionMode| is purely abstract, we only
// use dynamic dispatch when using the debug options. Almost always we
// directly call the member function on one of the statics.
//
// XXX: The macros underneath are clunky, largely because warnings
// regarding empty macro arguments are still enabled. We should like to
// write:
//
// #define DECLARE_ALL_OPS(QUALIFIERS, EXTRAS..)
// QUALIFIERS ExecutionStatus op(args, ##EXTRAS);
//
// And call it either like DECLARE_ALL_OPS() or DECLARE_ALL_OPS(virtual)
// or DECLARE_ALL_OPS(virtual, uint32_t limit). But unused macro arguments
// makes gcc warn gratuitously.
#define JS_PA_build_ARGS \
JSContext *cx, \
IndexInfo &iv, \
HandleObject elementalFun, \
HandleObject buffer
#define JS_PA_map_ARGS \
JSContext *cx, \
HandleParallelArrayObject source, \
HandleObject elementalFun, \
HandleObject buffer
#define JS_PA_reduce_ARGS \
JSContext *cx, \
HandleParallelArrayObject source, \
HandleObject elementalFun, \
HandleObject buffer, \
MutableHandleValue vp
#define JS_PA_scatter_ARGS \
JSContext *cx, \
HandleParallelArrayObject source, \
HandleObject targets, \
const Value &defaultValue, \
HandleObject conflictFun, \
HandleObject buffer
#define JS_PA_filter_ARGS \
JSContext *cx, \
HandleParallelArrayObject source, \
HandleObject filters, \
HandleObject buffer
#define JS_PA_DECLARE_OP(NAME) \
ExecutionStatus NAME(JS_PA_ ## NAME ## _ARGS)
#define JS_PA_DECLARE_CUSTOM_OP(NAME, BASE, EXTRAS...) \
ExecutionStatus NAME(JS_PA_ ## BASE ## _ARGS, ##EXTRAS)
#define JS_PA_DECLARE_ALL_OPS \
JS_PA_DECLARE_OP(build); \
JS_PA_DECLARE_OP(map); \
JS_PA_DECLARE_OP(reduce); \
JS_PA_DECLARE_OP(scatter); \
JS_PA_DECLARE_OP(filter);
class ExecutionMode {
public:
// The comprehension form. Builds a higher-dimensional array using a
// kernel function.
virtual JS_PA_DECLARE_OP(build) = 0;
// Maps a kernel function over the outermost dimension of the array.
virtual JS_PA_DECLARE_OP(map) = 0;
// Reduce to a value using a kernel function. Scan is like reduce, but
// keeps the intermediate results in an array.
virtual JS_PA_DECLARE_OP(reduce) = 0;
// Scatter elements according to an index map.
virtual JS_PA_DECLARE_OP(scatter) = 0;
// Filter elements according to a truthy array.
virtual JS_PA_DECLARE_OP(filter) = 0;
virtual const char *toString() = 0;
};
// Fallback means try parallel first, and if unable to execute in
// parallel, execute sequentially.
class FallbackMode : public ExecutionMode {
public:
JS_PA_DECLARE_ALL_OPS
const char *toString() { return "fallback"; }
};
class ParallelMode : public ExecutionMode {
public:
JS_PA_DECLARE_ALL_OPS
const char *toString() { return "parallel"; }
};
class SequentialMode : public ExecutionMode {
public:
JS_PA_DECLARE_ALL_OPS
const char *toString() { return "sequential"; }
};
static SequentialMode sequential;
static ParallelMode parallel;
static FallbackMode fallback;
#undef JS_PA_build_ARGS
#undef JS_PA_map_ARGS
#undef JS_PA_reduce_ARGS
#undef JS_PA_scatter_ARGS
#undef JS_PA_filter_ARGS
#undef JS_PA_DECLARE_OP
#undef JS_PA_DECLARE_CUSTOM_OP
#undef JS_PA_DECLARE_ALL_OPS
#ifdef DEBUG
// Debug options can be passed in as an extra argument to the
// operations. The grammar is:
//
// options ::= { mode: "par" | "seq",
// expect: "fail" | "bail" | "success" }
struct DebugOptions {
ExecutionMode *mode;
ExecutionStatus expect;
bool init(JSContext *cx, const Value &v);
bool check(JSContext *cx, ExecutionStatus actual);
};
static const char *ExecutionStatusToString(ExecutionStatus ss);
#endif
static JSFunctionSpec methods[];
static Class protoClass;
static inline bool DenseArrayToIndexVector(JSContext *cx, HandleObject obj,
IndexVector &indices);
bool toStringBufferImpl(JSContext *cx, IndexInfo &iv, bool useLocale,
HandleObject buffer, StringBuffer &sb);
static bool create(JSContext *cx, MutableHandleValue vp);
static bool create(JSContext *cx, HandleObject buffer, MutableHandleValue vp);
static bool create(JSContext *cx, HandleObject buffer, uint32_t offset,
const IndexVector &dims, MutableHandleValue vp);
static JSBool construct(JSContext *cx, unsigned argc, Value *vp);
static bool map(JSContext *cx, CallArgs args);
static bool reduce(JSContext *cx, CallArgs args);
static bool scan(JSContext *cx, CallArgs args);
static bool scatter(JSContext *cx, CallArgs args);
static bool filter(JSContext *cx, CallArgs args);
static bool flatten(JSContext *cx, CallArgs args);
static bool partition(JSContext *cx, CallArgs args);
static bool get(JSContext *cx, CallArgs args);
static bool dimensionsGetter(JSContext *cx, CallArgs args);
static bool lengthGetter(JSContext *cx, CallArgs args);
static bool toString(JSContext *cx, CallArgs args);
static bool toLocaleString(JSContext *cx, CallArgs args);
static bool toSource(JSContext *cx, CallArgs args);
static void mark(JSTracer *trc, JSObject *obj);
static JSBool lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleObject objp, MutableHandleShape propp);
static JSBool lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleObject objp, MutableHandleShape propp);
static JSBool lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleObject objp, MutableHandleShape propp);
static JSBool lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
MutableHandleObject objp, MutableHandleShape propp);
static JSBool defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs);
static JSBool defineProperty(JSContext *cx, HandleObject obj,
HandlePropertyName name, HandleValue value,
JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs);
static JSBool defineElement(JSContext *cx, HandleObject obj,
uint32_t index, HandleValue value,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
static JSBool defineSpecial(JSContext *cx, HandleObject obj,
HandleSpecialId sid, HandleValue value,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
static JSBool getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
HandleId id, MutableHandleValue vp);
static JSBool getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
HandlePropertyName name, MutableHandleValue vp);
static JSBool getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
uint32_t index, MutableHandleValue vp);
static JSBool getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
HandleSpecialId sid, MutableHandleValue vp);
static JSBool setGeneric(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue vp, JSBool strict);
static JSBool setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleValue vp, JSBool strict);
static JSBool setElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleValue vp, JSBool strict);
static JSBool setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
MutableHandleValue vp, JSBool strict);
static JSBool getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
unsigned *attrsp);
static JSBool getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
unsigned *attrsp);
static JSBool getElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
unsigned *attrsp);
static JSBool getSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid,
unsigned *attrsp);
static JSBool setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
unsigned *attrsp);
static JSBool setPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
unsigned *attrsp);
static JSBool setElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
unsigned *attrsp);
static JSBool setSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid,
unsigned *attrsp);
static JSBool deleteGeneric(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue rval, JSBool strict);
static JSBool deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleValue rval, JSBool strict);
static JSBool deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleValue rval, JSBool strict);
static JSBool deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
MutableHandleValue rval, JSBool strict);
static JSBool enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
Value *statep, jsid *idp);
};
} // namespace js
extern JSObject *
js_InitParallelArrayClass(JSContext *cx, JSObject *obj);
#endif // ParallelArray_h__

View File

@ -0,0 +1,9 @@
function buildComprehension() {
// 1D comprehension
var p = new ParallelArray(10, function (idx) { return idx; });
var a = [0,1,2,3,4,5,6,7,8,9];
assertEq(p.toString(), "<" + a.join(",") + ">");
}
buildComprehension();

View File

@ -0,0 +1,9 @@
function buildMultidim() {
// 2D comprehension
var p = new ParallelArray([2,2], function (i,j) { return i + j; });
assertEq(p.shape.toString(), [2,2].toString());
assertEq(p.toString(), "<<0,1>,<1,2>>");
}
buildMultidim();

View File

@ -0,0 +1,15 @@
function buildComprehension() {
// Test kernel function arguments
var shape = [];
for (var i = 0; i < 8; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function () {
assertEq(arguments.length, shape.length);
for (var j = 0; j < shape.length; j++)
assertEq(arguments[j] >= 0 && arguments[j] < shape[j], true);
});
}
}
buildComprehension();

View File

@ -0,0 +1,31 @@
function buildComprehension() {
var H = 96;
var W = 96;
var d = 4;
// 3D 96x96x4 texture-like PA
var p = new ParallelArray([H,W,d], function (i,j,k) { return i + j + k; });
var a = "<";
for (var i = 0; i < H; i++) {
a += "<";
for (var j = 0; j < W; j++) {
a += "<";
for (var k = 0; k < d; k++) {
a += i+j+k;
if (k !== d - 1)
a += ",";
}
a += ">";
if (j !== W - 1)
a += ","
}
a += ">";
if (i !== H - 1)
a += ","
}
a += ">"
assertEq(p.toString(), a);
}
buildComprehension();

View File

@ -0,0 +1,7 @@
// |jit-test| error: TypeError;
function buildComprehension() {
// Throws if elemental fun not callable
var p = new ParallelArray([2,2], undefined);
}
buildComprehension();

View File

@ -0,0 +1,16 @@
function bracket(s) {
return "<" + s + ">";
}
function buildSimple() {
// Simple constructor
var a = [1,2,3,4,5];
var p = new ParallelArray(a);
var e = a.join(",");
assertEq(p.toString(), bracket(e));
a[0] = 9;
// No sharing
assertEq(p.toString(), bracket(e));
}
buildSimple();

View File

@ -0,0 +1,13 @@
function buildWithHoles() {
// Test holes
var a = new Array(5);
for (var cnt = 0; cnt < a.length; cnt+=2) {
a[cnt] = cnt;
}
var b = [0,1,2,3,4];
var p = new ParallelArray(a);
assertEq(Object.keys(p).join(","), Object.keys(b).join(","));
}
buildWithHoles();

View File

@ -0,0 +1,13 @@
function bracket(s) {
return "<" + s + ">";
}
function buildArrayLike() {
// Construct copying from array-like
var a = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 };
var p = new ParallelArray(a);
var e = Array.prototype.join.call(a, ",");
assertEq(p.toString(), bracket(e));
}
buildArrayLike();

View File

@ -0,0 +1,12 @@
function buildPA() {
// Construct copying from PA
var p1 = new ParallelArray([1,2,3,4]);
var p2 = new ParallelArray(p1);
assertEq(p1.toString(), p2.toString());
var p1d = new ParallelArray([2,2], function(i,j) { return i + j; });
var p2d = new ParallelArray(p1d);
assertEq(p1d.toString(), p2d.toString());
}
buildPA();

View File

@ -0,0 +1,13 @@
function testElement() {
// Test getting element from 1D
var a = [1,{},"a",false]
var p = new ParallelArray(a);
for (var i = 0; i < a.length; i++) {
assertEq(p[i], p[i]);
assertEq(p[i], a[i]);
}
// Test out of bounds
assertEq(p[42], undefined);
}
testElement();

View File

@ -0,0 +1,11 @@
function testElement() {
// Test getting element from higher dimension
var p = new ParallelArray([2,2,2], function () { return 0; });
assertEq(p[0].toString(), "<<0,0>,<0,0>>");
// Should create new wrapper
assertEq(p[0] !== p[0], true);
// Test out of bounds
assertEq(p[42], undefined);
}
testElement();

View File

@ -0,0 +1,10 @@
function testElement() {
var p = new ParallelArray([9]);
var desc = Object.getOwnPropertyDescriptor(p, "0");
assertEq(desc.enumerable, true);
assertEq(desc.configurable, false);
assertEq(desc.writable, false);
assertEq(desc.value, 9);
}
testElement();

View File

@ -0,0 +1,8 @@
function testElement() {
// Test crazy prototype walking
ParallelArray.prototype[42] = "foo";
var p = new ParallelArray([1,2,3,4]);
assertEq(p[42], "foo");
}
testElement();

View File

@ -0,0 +1,7 @@
function testEnumerate() {
var p = new ParallelArray([1,2,3,4,5]);
for (var i in p)
assertEq(i >= 0 && i < p.length, true);
}
testEnumerate();

View File

@ -0,0 +1,13 @@
function testFilterAll() {
// Test filtering everything (leaving everything in)
var p = new ParallelArray([0,1,2,3,4]);
var all = p.map(function (i) { return true; });
var r = p.filter(all);
assertEq(r.toString(), "<0,1,2,3,4>");
var p = new ParallelArray([5,2], function(i,j) { return i+j; });
var r = p.filter(all);
assertEq(r.toString(), "<<0,1>,<1,2>,<2,3>,<3,4>,<4,5>>");
}
testFilterAll();

View File

@ -0,0 +1,12 @@
function testFilterNone() {
// Test filtering (removing everything)
var p = new ParallelArray([0,1,2,3,4]);
var none = p.map(function () { return false; });
var r = p.filter(none);
assertEq(r.toString(), "<>");
var p = new ParallelArray([5,2], function(i,j) { return i+j; });
var r = p.filter(none);
assertEq(r.toString(), "<>");
}
testFilterNone();

View File

@ -0,0 +1,16 @@
function bracket(s) {
return "<" + s + ">";
}
function testFilterSome() {
var p = new ParallelArray([0,1,2,3,4]);
var evenBelowThree = p.map(function (i) { return ((i%2) === 0) && (i < 3); });
var r = p.filter(evenBelowThree);
assertEq(r.toString(), bracket([0,2].join(",")));
var p = new ParallelArray([5,2], function (i,j) { return i; });
var evenBelowThree = p.map(function (i) { return ((i[0]%2) === 0) && (i[0] < 3); });
var r = p.filter(evenBelowThree);
assertEq(r.toString(), bracket(["<0,0>","<2,2>"].join(",")));
}
testFilterSome();

View File

@ -0,0 +1,18 @@
function bracket(s) {
return "<" + s + ">";
}
function testFilterMisc() {
var p = new ParallelArray([0,1,2]);
// Test array
var r = p.filter([true, false, true]);
assertEq(r.toString(), bracket([0,2].join(",")));
// Test array-like
var r = p.filter({ 0: true, 1: false, 2: true, length: 3 });
assertEq(r.toString(), bracket([0,2].join(",")));
// Test truthy
var r = p.filter([1, "", {}]);
assertEq(r.toString(), bracket([0,2].join(",")));
}
testFilterMisc();

View File

@ -0,0 +1,11 @@
function testFlatten() {
var shape = [5];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function(i,j) { return i+j; });
var flatShape = ([shape[0] * shape[1]]).concat(shape.slice(2));
assertEq(p.flatten().shape.toString(), flatShape.toString());
}
}
testFlatten();

View File

@ -0,0 +1,7 @@
function testFlatten() {
var p = new ParallelArray([2,2], function(i,j) { return i+j; });
var p2 = new ParallelArray([0,1,1,2]);
assertEq(p.flatten().toString(), p2.toString());
}
testFlatten();

View File

@ -0,0 +1,9 @@
// |jit-test| error: Error;
function testFlattenFlat() {
// Throw on flattening flat array
var p = new ParallelArray([1]);
var f = p.flatten();
}
testFlattenFlat();

View File

@ -0,0 +1,8 @@
function testGet() {
var a = [1,2,3,4,5];
var p = new ParallelArray(a);
for (var i = 0; i < a.length; i++)
assertEq(p.get([i]), a[i]);
}
testGet();

View File

@ -0,0 +1,10 @@
function testGet() {
var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; });
assertEq(p.get([1,1,1]), 1+1+1);
var p2 = new ParallelArray([2], function(i) { return 1+1+i; });
assertEq(p.get([1,1]).toString(), p2.toString());
var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; });
assertEq(p.get([1]).toString(), p3.toString());
}
testGet();

View File

@ -0,0 +1,8 @@
function testGetNoCraziness() {
// .get shouldn't do prototype walks
ParallelArray.prototype[42] = "foo";
var p = new ParallelArray([1,2,3,4]);
assertEq(p.get([42]), undefined);
}
testGetNoCraziness();

View File

@ -0,0 +1,6 @@
function testGetBounds() {
var p = new ParallelArray([1,2,3,4]);
assertEq(p.get([42]), undefined);
}
testGetBounds();

View File

@ -0,0 +1,9 @@
function testGet() {
// Test .get on array-like
var p = new ParallelArray([1,2,3,4]);
assertEq(p.get({ 0: 1, length: 1 }), 2);
var p2 = new ParallelArray([2,2], function(i,j) { return i+j; });
assertEq(p2.get({ 0: 1, 1: 0, length: 2 }), 1);
}
testGet();

View File

@ -0,0 +1,9 @@
// |jit-test| error: TypeError;
function testGetThrows() {
// Throw if argument not object
var p = new ParallelArray([1,2,3,4]);
p.get(42);
}
testGetThrows();

View File

@ -0,0 +1,11 @@
function testLength() {
var a = [1,2,3];
var p = new ParallelArray(a);
assertEq(p.length, a.length);
// Test holes
var a = [1,,3];
var p = new ParallelArray(a);
assertEq(p.length, a.length);
}
testLength();

View File

@ -0,0 +1,12 @@
function testLength() {
// Test higher dimension shape up to 8D
var shape = [];
for (var i = 0; i < 8; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function () { return 0; });
// Length should be outermost dimension
assertEq(p.length, shape[0]);
}
}
testLength();

View File

@ -0,0 +1,11 @@
function testLength() {
// Test length attributes
var p = new ParallelArray([1,2,3,4]);
var desc = Object.getOwnPropertyDescriptor(p, "length");
assertEq(desc.enumerable, false);
assertEq(desc.configurable, false);
assertEq(desc.writable, false);
assertEq(desc.value, 4);
}
testLength();

View File

@ -0,0 +1,11 @@
function bracket(s) {
return "<" + s + ">";
}
function testMap() {
var p = new ParallelArray([0,1,2,3,4]);
var m = p.map(function (v) { return v+1; });
assertEq(m.toString(), bracket([1,2,3,4,5].join(",")));
}
testMap();

View File

@ -0,0 +1,9 @@
function testMap() {
// Test overflow
var p = new ParallelArray([1 << 30]);
var m = p.map(function(x) { return x * 4; });
assertEq(m[0], (1 << 30) * 4);
}
testMap();

View File

@ -0,0 +1,9 @@
function testMap() {
// Test mapping higher dimensional
var p = new ParallelArray([2,2], function (i,j) { return i+j; });
var m = p.map(function(x) { return x.toString(); });
assertEq(m.toString(), "<<0,1>,<1,2>>");
}
testMap();

View File

@ -0,0 +1,19 @@
function testMap() {
// Test map elemental fun args
var p = new ParallelArray([1,2,3,4]);
var m = p.map(function(e) {
assertEq(e >= 1 && e <= 4, true);
});
var m = p.map(function(e,i) {
assertEq(e >= 1 && e <= 4, true);
assertEq(i >= 0 && i < 4, true);
});
var m = p.map(function(e,i,c) {
assertEq(e >= 1 && e <= 4, true);
assertEq(i >= 0 && i < 4, true);
assertEq(c, p);
});
}
testMap();

View File

@ -0,0 +1,9 @@
// |jit-test| error: TypeError;
function testMap() {
// Test map throws
var p = new ParallelArray([1,2,3,4])
var m = p.map(42);
}
testMap();

View File

@ -0,0 +1,13 @@
function testPartition() {
var p = new ParallelArray([1,2,3,4,5,6,7,8]);
var pp = p.partition(2);
var ppp = pp.partition(2);
var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1));
var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1));
assertEq(pp.shape.toString(), ppShape.toString())
assertEq(pp.toString(), "<<1,2>,<3,4>,<5,6>,<7,8>>");
assertEq(ppp.shape.toString(), pppShape.toString())
assertEq(ppp.toString(), "<<<1,2>,<3,4>>,<<5,6>,<7,8>>>");
}
testPartition();

View File

@ -0,0 +1,8 @@
// |jit-test| error: Error;
function testPartitionDivisible() {
var p = new ParallelArray([1,2,3,4]);
var pp = p.partition(3);
}
testPartitionDivisible();

View File

@ -0,0 +1,8 @@
function testReduce() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.reduce(function (v, p) { return v*p; });
assertEq(r, 120);
}
testReduce();

View File

@ -0,0 +1,8 @@
function testReduceOne() {
var p = new ParallelArray([1]);
var r = p.reduce(function (v, p) { return v*p; });
assertEq(r, 1);
}
testReduceOne();

View File

@ -0,0 +1,14 @@
function testReduce() {
// Test reduce on higher dimensional
// XXX Can we test this better?
var shape = [2];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function () { return i+1; });
var r = p.reduce(function (a, b) { return a; });
assertEq(r.shape.length, i + 1);
}
}
testReduce();

View File

@ -0,0 +1,13 @@
function testReduce() {
// Test reduce elemental fun args
var p = new ParallelArray([1,2,3,4]);
var r = p.reduce(function (a, b) {
assertEq(a >= 1 && a <= 4, true);
assertEq(b >= 1 && b <= 4, true);
return a;
});
assertEq(r >= 1 && r <= 4, true);
}
testReduce();

View File

@ -0,0 +1,16 @@
load(libdir + "asserts.js");
function testReduceThrows() {
// Throw on empty
assertThrowsInstanceOf(function () {
var p = new ParallelArray([]);
p.reduce(function (v, p) { return v*p; });
}, Error);
// Throw on not function
assertThrowsInstanceOf(function () {
var p = new ParallelArray([1]);
p.reduce(42);
}, TypeError);
}
testReduceThrows();

View File

@ -0,0 +1,13 @@
function testScan() {
function f(v, p) { return v*p; }
var a = [1,2,3,4,5];
var p = new ParallelArray(a);
var s = p.scan(f);
for (var i = 0; i < p.length; i++) {
var p2 = new ParallelArray(a.slice(0, i+1));
assertEq(s[i], p2.reduce(f));
}
}
testScan();

View File

@ -0,0 +1,9 @@
function testScanOne() {
function f(v, p) { return v*p; }
var p = new ParallelArray([1]);
var s = p.scan(f);
assertEq(s[0], p.reduce(f));
}
testScanOne();

View File

@ -0,0 +1,18 @@
function testScan() {
// Test reduce on higher dimensional
// XXX Can we test this better?
function f(a, b) { return a; }
var shape = [2];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function () { return i+1; });
var r = p.reduce(f);
var s = p.scan(f)
for (var j = 0; j < s.length; j++)
assertEq(s[0].shape.length, i + 1);
assertEq(r.shape.length, i + 1);
}
}
testScan();

View File

@ -0,0 +1,13 @@
function testScan() {
// Test reduce elemental fun args
var p = new ParallelArray([1,2,3,4]);
var r = p.reduce(function (a, b) {
assertEq(a >= 1 && a <= 4, true);
assertEq(b >= 1 && b <= 4, true);
return a;
});
assertEq(r >= 1 && r <= 4, true);
}
testScan();

View File

@ -0,0 +1,16 @@
load(libdir + "asserts.js");
function testScanThrows() {
// Throw on empty
assertThrowsInstanceOf(function () {
var p = new ParallelArray([]);
p.scan(function (v, p) { return v*p; });
}, Error);
// Throw on not function
assertThrowsInstanceOf(function () {
var p = new ParallelArray([1]);
p.scan(42);
}, TypeError);
}
testScanThrows();

View File

@ -0,0 +1,8 @@
function testScatter() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10);
assertEq(r.length, 10);
}
testScatter();

View File

@ -0,0 +1,9 @@
function testScatterIdentity() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,2,3,4]);
assertEq(p.toString(), r.toString());
}
testScatterIdentity();

View File

@ -0,0 +1,10 @@
function testScatter() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([1,0,3,2,4]);
var p2 = new ParallelArray([2,1,4,3,5]);
assertEq(r.toString(), p2.toString());
}
testScatter();

View File

@ -0,0 +1,12 @@
function bracket(s) {
return "<" + s + ">";
}
function testScatterDefault() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,2,4], 9);
assertEq(r.toString(), bracket([1,9,2,9,3].join(",")));
}
testScatterDefault();

View File

@ -0,0 +1,11 @@
function bracket(s) {
return "<" + s + ">";
}
function testScatterConflict() {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; });
assertEq(r.toString(), bracket([4,2,9,4,5].join(",")));
}
testScatterConflict();

View File

@ -0,0 +1,13 @@
function testScatter() {
// Test scatter on higher dimension
var shape = [5];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function(k) { return k; });
var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; }, 10);
assertEq(r.length, 10);
}
}
testScatter();

View File

@ -0,0 +1,13 @@
function testScatterIdentity() {
var shape = [5];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function(k) { return k; });
var r = p.scatter([0,1,2,3,4]);
assertEq(p.toString(), r.toString());
}
}
testScatterIdentity();

View File

@ -0,0 +1,14 @@
function testScatter() {
var shape = [5];
for (var i = 0; i < 7; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function(k) { return k; });
var r = p.scatter([1,0,3,2,4]);
var p2 = new ParallelArray([p[1], p[0], p[3], p[2], p[4]]);
assertEq(p2.toString(), r.toString());
}
}
testScatter();

View File

@ -0,0 +1,7 @@
function testScatter() {
// Ignore the rest of the scatter vector if longer than source
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([1,0,3,2,4,1,2,3]);
var p2 = new ParallelArray([2,1,4,3,5]);
assertEq(r.toString(), p2.toString());
}

View File

@ -0,0 +1,16 @@
load(libdir + "asserts.js");
function testScatterThrows() {
// Throw on conflict with no resolution function
assertThrowsInstanceOf(function () {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,4]);
}, Error);
// Throw on out of bounds
assertThrowsInstanceOf(function () {
var p = new ParallelArray([1,2,3,4,5]);
var r = p.scatter([0,1,0,3,11]);
}, Error);
}
testScatterThrows();

View File

@ -0,0 +1,8 @@
function testShape() {
var a = [1,2,3];
var p = new ParallelArray(a);
assertEq(p.shape.length, 1);
assertEq(p.shape[0], a.length);
}
testShape();

View File

@ -0,0 +1,14 @@
function testShape() {
// Test higher dimension shape up to 8D
var shape = [];
for (var i = 0; i < 8; i++) {
shape.push(i+1);
var p = new ParallelArray(shape, function () { return 0; });
// Test shape identity and shape
assertEq(p.shape, p.shape);
assertEq(p.shape !== shape, true);
assertEq(p.shape.toString(), shape.toString());
}
}
testShape();

View File

@ -0,0 +1,37 @@
// ParallelArray surfaces
var desc = Object.getOwnPropertyDescriptor(this, "ParallelArray");
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
assertEq(desc.writable, true);
assertEq(typeof ParallelArray, 'function');
assertEq(Object.keys(ParallelArray).length, 0);
assertEq(ParallelArray.length, 0);
assertEq(ParallelArray.name, "ParallelArray");
assertEq(Object.getPrototypeOf(ParallelArray.prototype), Object.prototype);
assertEq(Object.prototype.toString.call(ParallelArray.prototype), "[object ParallelArray]");
assertEq(Object.prototype.toString.call(new ParallelArray), "[object ParallelArray]");
assertEq(Object.prototype.toString.call(ParallelArray()), "[object ParallelArray]");
assertEq(Object.keys(ParallelArray.prototype).join(), "");
assertEq(ParallelArray.prototype.constructor, ParallelArray);
function checkMethod(name, arity) {
var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name);
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
assertEq(desc.writable, true);
assertEq(typeof desc.value, 'function');
assertEq(desc.value.name, name);
assertEq(desc.value.length, arity);
}
checkMethod("map", 1);
checkMethod("reduce", 1);
checkMethod("scan", 1);
checkMethod("scatter", 1);
checkMethod("filter", 1);
checkMethod("flatten", 0);
checkMethod("partition", 1);
checkMethod("get", 1);

View File

@ -0,0 +1,27 @@
// ParallelArray methods throw when passed a this-value that isn't a ParallelArray.
load(libdir + "asserts.js");
function testcase(obj, fn) {
assertEq(typeof fn, "function");
var args = Array.slice(arguments, 2);
assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError);
}
function test(obj) {
function f() {}
testcase(obj, ParallelArray.prototype.map, f);
testcase(obj, ParallelArray.prototype.reduce, f);
testcase(obj, ParallelArray.prototype.scan, f);
testcase(obj, ParallelArray.prototype.scatter, [0]);
testcase(obj, ParallelArray.prototype.filter, [0]);
testcase(obj, ParallelArray.prototype.flatten);
testcase(obj, ParallelArray.prototype.partition, 2);
testcase(obj, ParallelArray.prototype.get, [1]);
}
test(ParallelArray.prototype);
test(Object.create(new ParallelArray));
test({});
test(null);
test(undefined);

View File

@ -353,3 +353,10 @@ MSG_DEF(JSMSG_REST_WITH_DEFAULT, 299, 0, JSEXN_SYNTAXERR, "rest parameter m
MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression")
MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}")
MSG_DEF(JSMSG_PAR_ARRAY_IMMUTABLE, 303, 0, JSEXN_TYPEERR, "ParallelArray objects are immutable")
MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument")
MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension")
MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object")
MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object")
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")

View File

@ -55,6 +55,7 @@
#include "builtin/Eval.h"
#include "builtin/MapObject.h"
#include "builtin/RegExp.h"
#include "builtin/ParallelArray.h"
#include "ds/LifoAlloc.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/TreeContext.h"
@ -1865,6 +1866,7 @@ static JSStdName standard_class_atoms[] = {
{js_InitWeakMapClass, EAGER_CLASS_ATOM(WeakMap), &js::WeakMapClass},
{js_InitMapClass, EAGER_CLASS_ATOM(Map), &js::MapObject::class_},
{js_InitSetClass, EAGER_CLASS_ATOM(Set), &js::SetObject::class_},
{js_InitParallelArrayClass, EAGER_CLASS_ATOM(ParallelArray), &js::ParallelArrayObject::class_},
{NULL, 0, NULL}
};

View File

@ -122,6 +122,7 @@ DEFINE_PROTOTYPE_ATOM(WeakMap)
DEFINE_ATOM(buffer, "buffer")
DEFINE_ATOM(byteLength, "byteLength")
DEFINE_ATOM(byteOffset, "byteOffset")
DEFINE_ATOM(shape, "shape")
DEFINE_KEYWORD_ATOM(return)
DEFINE_KEYWORD_ATOM(throw)
DEFINE_ATOM(url, "url")

View File

@ -64,6 +64,7 @@ JS_PROTO(WeakMap, 36, js_InitWeakMapClass)
JS_PROTO(Map, 37, js_InitMapClass)
JS_PROTO(Set, 38, js_InitSetClass)
JS_PROTO(DataView, 39, js_InitTypedArrayClasses)
JS_PROTO(ParallelArray, 40, js_InitParallelArrayClass)
#undef XML_INIT
#undef NAMESPACE_INIT