Hi all --
This is a request for a review to simplify the implementation of standard
operations (+, -, *, etc.) on homogeneous tuple operations. It's pretty
straightforward. I thought I was seeing a bit of performance improvement
on my Mac last night, but on linux it seems to be a bit of a wash.
However, looking at the generated code, it seems like a no-brainer -- it
makes it simpler and more straightforward. Patch attached, proposed log
message below.
-Brad
----
Simplify the implementation of homogeneous tuple operations
Prior to this commit, we have been implementing all tuple operations (+,
-, *, etc.) on singleton tuples, pairs of tuples, and tuple-scalar pairs
using a recursive Lisp-y style of implementation approach. While this
approach is reasonably elegant to write and very general with respect to
the tuple types, the code it generates is pretty ugly. For example, a
tuple addition like this:
var x: 3*real = (1.0, 2.0, 3.0);
var y: 3*real = (4.0, 5.0, 6.0);
var z = x + y;
ends up looking like this:
call_tmp = (1.0 + 4.0);
ret_to_arg_ref_tmp_ = &wrap_call_tmp;
chpl__tupleRestHelper2(1.0, 2.0, 3.0, ret_to_arg_ref_tmp_);
ret_to_arg_ref_tmp_2 = &wrap_call_tmp2;
chpl__tupleRestHelper2(4.0, 5.0, 6.0, ret_to_arg_ref_tmp_2);
call_tmp2 = *(wrap_call_tmp + INT64(0));
call_tmp3 = *(wrap_call_tmp2 + INT64(0));
call_tmp4 = (call_tmp2 + call_tmp3);
call_tmp5 = *(wrap_call_tmp + INT64(0));
call_tmp6 = *(wrap_call_tmp + INT64(1));
ret_to_arg_ref_tmp_3 = &wrap_call_tmp3;
chpl__tupleRestHelper(call_tmp5, call_tmp6, ret_to_arg_ref_tmp_3);
call_tmp7 = *(wrap_call_tmp2 + INT64(0));
call_tmp8 = *(wrap_call_tmp2 + INT64(1));
ret_to_arg_ref_tmp_4 = &wrap_call_tmp4;
chpl__tupleRestHelper(call_tmp7, call_tmp8, ret_to_arg_ref_tmp_4);
call_tmp9 = *(wrap_call_tmp3 + INT64(0));
call_tmp10 = *(wrap_call_tmp4 + INT64(0));
call_tmp11 = (call_tmp9 + call_tmp10);
*(z + INT64(0)) = call_tmp;
*(z + INT64(1)) = call_tmp4;
*(z + INT64(2)) = call_tmp11;
In wrestling through generated code idioms like this for homogeneous
tuples, I realized that there's no need to use the recursive approach
because it's straightforward to anticipate the result type required,
declare a variable for it, and fill the result using a param loop. This
results in much cleaner generated code, like this:
call_tmp = (1.0 + 4.0);
call_tmp2 = (2.0 + 5.0);
call_tmp3 = (3.0 + 6.0);
*(z + INT64(0)) = call_tmp;
*(z + INT64(1)) = call_tmp2;
*(z + INT64(2)) = call_tmp3;
The primary challenge to doing the similar thing for heterogeneous tuples
is that it's difficult to declare the result type using Chapel code today.
For homogeneous operations, we can say things like:
var result: a.size*(a(1) + b(1)).type;
because we know that the type resulting from adding the 1st component will
apply to all the others as well. But for a heterogeneous case, we can't
anticipate how many fields the tuple will have and don't have a way for
mapping or expanding type-based computations like this across tuples (at
least that I've been able to come up with so far).
So, in this commit, I provide specialized overloads of the operations for
the (common) cases when the tuple(s) are homogeneous and preserve the
original implementation for the fall-through case when one or both tuples
are heterogeneous.
My next step is to look at eliminating the tuple temps that are used when
assigning tuples, which result from the combination of normalization and
the fact that our assignment operators don't take the LHS by reference.
This should go even further in cleaning up tuple-based code.Index: modules/internal/ChapelTuple.chpl
===================================================================
--- modules/internal/ChapelTuple.chpl (revision 22445)
+++ modules/internal/ChapelTuple.chpl (working copy)
@@ -208,7 +208,7 @@
// tuple except the first
//
inline proc chpl__tupleRest(t: _tuple) {
- proc chpl__tupleRestHelper(first, rest...)
+ inline proc chpl__tupleRestHelper(first, rest...)
return rest;
return chpl__tupleRestHelper((...t));
}
@@ -216,6 +216,14 @@
//
// standard overloaded unary operators on tuples
//
+
+ inline proc +(a: _tuple) where isHomogeneousTuple(a) {
+ var result: a.type;
+ for param d in 1..a.size do
+ result(d) = +a(d);
+ return result;
+ }
+
inline proc +(a: _tuple) {
if a.size == 1 then
return (+a(1), );
@@ -223,12 +231,26 @@
return (+a(1), (...+chpl__tupleRest(a)));
}
+ inline proc -(a: _tuple) where isHomogeneousTuple(a) {
+ var result: a.type;
+ for param d in 1..a.size do
+ result(d) = -a(d);
+ return result;
+ }
+
inline proc -(a: _tuple) {
if a.size == 1 then
- return (-a(1),);
+ return (-a(1), );
else
return (-a(1), (...-chpl__tupleRest(a)));
}
+
+ inline proc ~(a: _tuple) where isHomogeneousTuple(a) {
+ var result: a.type;
+ for param d in 1..a.size do
+ result(d) = ~a(d);
+ return result;
+ }
inline proc ~(a: _tuple) {
if a.size == 1 then
@@ -237,6 +259,13 @@
return (~a(1), (...~chpl__tupleRest(a)));
}
+ inline proc !(a: _tuple) where isHomogeneousTuple(a) {
+ var result: a.type;
+ for param d in 1..a.size do
+ result(d) = !a(d);
+ return result;
+ }
+
inline proc !(a: _tuple) {
if a.size == 1 then
return (!a(1),);
@@ -266,6 +295,28 @@
//
// standard overloaded binary operators on tuples
//
+
+ proc chpl_TwoHomogTuples(t1, t2) param {
+ return isHomogeneousTuple(t1) && isHomogeneousTuple(t2);
+ }
+
+ //
+ // Case optimized for homogeneous tuples
+ //
+ inline proc +(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to + have different sizes");
+
+ var result: a.size*(a(1) + b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) + b(d);
+
+ return result;
+ }
+
+ //
+ // General case for heterogeneous tuples
+ //
inline proc +(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to + have different sizes");
@@ -274,6 +325,18 @@
else
return (a(1)+b(1), (...chpl__tupleRest(a)+chpl__tupleRest(b)));
}
+
+
+ inline proc -(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to - have different sizes");
+
+ var result: a.size*(a(1) - b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) - b(d);
+
+ return result;
+ }
inline proc -(a: _tuple, b: _tuple) {
if a.size != b.size then
@@ -283,6 +346,18 @@
else
return (a(1)-b(1), (...chpl__tupleRest(a)-chpl__tupleRest(b)));
}
+
+
+ inline proc *(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to * have different sizes");
+
+ var result: a.size*(a(1) * b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) * b(d);
+
+ return result;
+ }
inline proc *(a: _tuple, b: _tuple) {
if a.size != b.size then
@@ -293,6 +368,17 @@
return (a(1)*b(1), (...chpl__tupleRest(a)*chpl__tupleRest(b)));
}
+ inline proc /(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to / have different sizes");
+
+ var result: a.size*(a(1) / b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) / b(d);
+
+ return result;
+ }
+
inline proc /(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to / have different sizes");
@@ -302,6 +388,17 @@
return (a(1)/b(1), (...chpl__tupleRest(a)/chpl__tupleRest(b)));
}
+ inline proc %(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to % have different sizes");
+
+ var result: a.size*(a(1) % b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) % b(d);
+
+ return result;
+ }
+
inline proc %(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to % have different sizes");
@@ -311,6 +408,17 @@
return (a(1)%b(1), (...chpl__tupleRest(a)%chpl__tupleRest(b)));
}
+ inline proc **(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to ** have different sizes");
+
+ var result: a.size*(a(1) ** b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) ** b(d);
+
+ return result;
+ }
+
inline proc **(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to ** have different sizes");
@@ -320,6 +428,17 @@
return (a(1)**b(1), (...chpl__tupleRest(a)**chpl__tupleRest(b)));
}
+ inline proc &(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to & have different sizes");
+
+ var result: a.size*(a(1) & b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) & b(d);
+
+ return result;
+ }
+
inline proc &(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to & have different sizes");
@@ -329,6 +448,17 @@
return (a(1)&b(1), (...chpl__tupleRest(a)&chpl__tupleRest(b)));
}
+ inline proc |(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to | have different sizes");
+
+ var result: a.size*(a(1) | b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) | b(d);
+
+ return result;
+ }
+
inline proc |(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to | have different sizes");
@@ -338,6 +468,17 @@
return (a(1)|b(1), (...chpl__tupleRest(a)|chpl__tupleRest(b)));
}
+ inline proc ^(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to ^ have different sizes");
+
+ var result: a.size*(a(1) ^ b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) ^ b(d);
+
+ return result;
+ }
+
inline proc ^(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to ^ have different sizes");
@@ -347,6 +488,17 @@
return (a(1)^b(1), (...chpl__tupleRest(a)^chpl__tupleRest(b)));
}
+ inline proc <<(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to << have different sizes");
+
+ var result: a.size*(a(1) << b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) << b(d);
+
+ return result;
+ }
+
inline proc <<(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to << have different sizes");
@@ -356,6 +508,17 @@
return (a(1)<<b(1), (...chpl__tupleRest(a)<<chpl__tupleRest(b)));
}
+ inline proc >>(a: _tuple, b: _tuple) where chpl_TwoHomogTuples(a,b) {
+ if a.size != b.size then
+ compilerError("tuple operands to >> have different sizes");
+
+ var result: a.size*(a(1) >> b(1)).type;
+ for param d in 1..a.size do
+ result(d) = a(d) >> b(d);
+
+ return result;
+ }
+
inline proc >>(a: _tuple, b: _tuple) {
if a.size != b.size then
compilerError("tuple operands to >> have different sizes");
@@ -434,168 +597,168 @@
// standard overloaded binary operators on homog tuple / scalar pairs
inline proc +(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)+y,);
- else
- return (x(1)+y, (...chpl__tupleRest(x)+y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) + y;
+ return result;
}
inline proc +(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x+y(1),);
- else
- return (x+y(1), (...x+chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x + y(d);
+ return result;
}
inline proc -(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)-y,);
- else
- return (x(1)-y, (...chpl__tupleRest(x)-y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) - y;
+ return result;
}
inline proc -(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x-y(1),);
- else
- return (x-y(1), (...x-chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x - y(d);
+ return result;
}
inline proc *(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)*y,);
- else
- return (x(1)*y, (...chpl__tupleRest(x)*y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) * y;
+ return result;
}
inline proc *(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
x: (y(1).type) {
- if y.size == 1 then
- return (x*y(1),);
- else
- return (x*y(1), (...x*chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x * y(d);
+ return result;
}
inline proc /(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)/y,);
- else
- return (x(1)/y, (...chpl__tupleRest(x)/y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) / y;
+ return result;
}
inline proc /(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x/y(1),);
- else
- return (x/y(1), (...x/chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x / y(d);
+ return result;
}
inline proc %(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)%y,);
- else
- return (x(1)%y, (...chpl__tupleRest(x)%y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) % y;
+ return result;
}
inline proc %(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x%y(1),);
- else
- return (x%y(1), (...x%chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x % y(d);
+ return result;
}
inline proc **(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)**y,);
- else
- return (x(1)**y, (...chpl__tupleRest(x)**y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) ** y;
+ return result;
}
inline proc **(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x**y(1),);
- else
- return (x**y(1), (...x**chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x ** y(d);
+ return result;
}
inline proc &(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)&y,);
- else
- return (x(1)&y, (...chpl__tupleRest(x)&y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) & y;
+ return result;
}
inline proc &(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x&y(1),);
- else
- return (x&y(1), (...x&chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x & y(d);
+ return result;
}
inline proc |(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)|y,);
- else
- return (x(1)|y, (...chpl__tupleRest(x)|y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) | y;
+ return result;
}
inline proc |(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x|y(1),);
- else
- return (x|y(1), (...x|chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x | y(d);
+ return result;
}
inline proc ^(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)^y,);
- else
- return (x(1)^y, (...chpl__tupleRest(x)^y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) ^ y;
+ return result;
}
inline proc ^(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x^y(1),);
- else
- return (x^y(1), (...x^chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x ^ y(d);
+ return result;
}
inline proc <<(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)<<y,);
- else
- return (x(1)<<y, (...chpl__tupleRest(x)<<y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) << y;
+ return result;
}
inline proc <<(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x<<y(1),);
- else
- return (x<<y(1), (...x<<chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x << y(d);
+ return result;
}
inline proc >>(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
- if x.size == 1 then
- return (x(1)>>y,);
- else
- return (x(1)>>y, (...chpl__tupleRest(x)>>y));
+ var result: x.size * x(1).type;
+ for param d in 1..x.size do
+ result(d) = x(d) >> y;
+ return result;
}
inline proc >>(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
t: (y(1).type) {
- if y.size == 1 then
- return (x>>y(1),);
- else
- return (x>>y(1), (...x>>chpl__tupleRest(y)));
+ var result: y.size * y(1).type;
+ for param d in 1..y.size do
+ result(d) = x >> y(d);
+ return result;
}
------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT
organizations don't have a clear picture of how application performance
affects their revenue. With AppDynamics, you get 100% visibility into your
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Chapel-developers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/chapel-developers