Skip to content

Commit 6d9f0c7

Browse files
committed
Implement sparsity support in existing operations
Tasks have to assemble the sparsity of their input nodes into a SparsityInfo and use that to create their result node. A SparsityManager is used to populate the sparsity information assembled on the host to the device tensor. Host tensors manage sparsity using a range-based encoding. TensorViews use an array-based representation where the sparsity information is encoded in the same memory as the tensor payload. That way we can transfer it directly between processes. Everything that can be sparse derives from one of the sparsity base classes (tensor, tensorviews) and coordinate with the base class the layout of the sparsity information. The sparsity information of Tensors, TensorViews, and function nodes can be accessed using the `sparsity()` member call. This makes heavy use of the conceptification of the API to pun away the sparsity base classes. TensorView should not be used directly. DenseTensorView is an alias for a dense TensorView. Similar aliases exist for sparse tensors. Where possible, concepts should be used. Also tightens the rules on const-ness in TensorView so that we cannot assign from a `const TensorView` to a non-const TensorView. Instead, we have to use a `TensorView<const T>` to adhere to constness. Signed-off-by: Joseph Schuchart <joseph.schuchart@stonybrook.edu>
1 parent 8e4cae9 commit 6d9f0c7

20 files changed

Lines changed: 536 additions & 117 deletions

include/mra/kernels/compress.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,24 @@ namespace mra {
7070
T* d_sumsq,
7171
const concepts::TensorViewArray<NDIM+1, Key<NDIM>::num_children()> auto in_views)
7272
{
73-
const bool is_t0 = (0 == thread_id());
7473
const size_type K2NDIM = std::pow( K,NDIM);
7574
const size_type TWOK2NDIM = std::pow(2*K,NDIM);
76-
using tensorview_t = decltype(p_in(0));
7775
SHARED std::array<decltype(in_views[0](0)), Key<NDIM>::num_children()> block_in_views;
7876
SHARED T* workspace;
79-
SHARED tensorview_t s, p, d;
77+
SHARED DenseTensorView<T, NDIM> s, p, d;
8078
int blockId = blockIdx.x;
8179
T* block_tmp = &tmp[blockId*compress_tmp_size<NDIM>(K)];
8280

83-
if (is_t0) {
84-
s = tensorview_t(&block_tmp[0], 2*K);
81+
if (is_team_lead()) {
82+
s = DenseTensorView<T, NDIM>(&block_tmp[0], 2*K);
8583
workspace = &block_tmp[TWOK2NDIM];
8684
}
87-
85+
assert(result_in.is_any_nonzero() && "why did we even get here?!");
8886
for (size_type fnid = blockId; fnid < N; fnid += gridDim.x) {
89-
/* no need to sync threads here */
90-
if (is_t0) {
87+
if (result_in.is_zero(fnid)) {
88+
continue; // output is zero so skip computation and leave it zero
89+
}
90+
if (is_team_lead()) {
9191
for (int i = 0; i < Key<NDIM>::num_children(); ++i) {
9292
block_in_views[i] = in_views[i](fnid);
9393
}

include/mra/kernels/derivative.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ namespace mra {
267267
size_type blockId = blockIdx.x;
268268
T* block_tmp_ptr = &tmp[blockId*derivative_tmp_size<NDIM>(K)];
269269
const size_type K2NDIM = std::pow(K, NDIM);
270-
if(is_team_lead()){
270+
if (is_team_lead()) {
271271
tmp_result = DenseTensorView<T, NDIM+1>(&block_tmp_ptr[ 0], make_dims<NDIM+1>(2, K));
272272
left_tmp = DenseTensorView<T, NDIM>(&block_tmp_ptr[2*K2NDIM], K);
273273
center_tmp = DenseTensorView<T, NDIM>(&block_tmp_ptr[3*K2NDIM], K);
@@ -318,8 +318,13 @@ namespace mra {
318318
static_assert(node_center.ndim() == NDIM+1, "node_center must be of dimension NDIM+1");
319319
static_assert(node_right.ndim() == NDIM+1, "node_right must be of dimension NDIM+1");
320320

321-
SHARED DenseTensorView<T, NDIM> node_left_view, node_center_view, node_right_view, deriv_view;
321+
SHARED DenseTensorView<T, NDIM> deriv_view;
322+
SHARED DenseTensorView<const T, NDIM> node_left_view, node_center_view, node_right_view;
322323
for (size_type blockid = blockIdx.x; blockid < N; blockid += gridDim.x) {
324+
if (deriv.is_zero(blockid)) {
325+
/* nothing to do */
326+
continue;
327+
}
323328
if (is_team_lead()) {
324329
node_left_view = node_left(blockid);
325330
node_center_view = node_center(blockid);

include/mra/kernels/fcoeffs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ namespace mra {
140140

141141
/* adjust pointers for the function of each block */
142142
for (size_type fnid = blockIdx.x; fnid < N; fnid += gridDim.x) {
143+
if (coeffs_view.is_zero(fnid)) {
144+
if (is_team_lead()) is_leaf[fnid] = false;
145+
continue; // skip sparse entries
146+
}
143147
if (is_team_lead()) {
144148
/* get the coefficient inputs */
145149
coeffs = coeffs_view(fnid);

include/mra/kernels/gaxpy.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ namespace mra {
3333
const T scalarB,
3434
size_type N)
3535
{
36-
SHARED DenseTensorView<T, NDIM> nodeA, nodeB, nodeR;
36+
SHARED DenseTensorView<const T, NDIM> nodeA, nodeB;
37+
SHARED DenseTensorView<T, NDIM> nodeR;
3738
for (size_type blockid = blockIdx.x; blockid < N; blockid += gridDim.x) {
39+
if (nodeR_view.is_zero(blockid)) {
40+
/* no work to do */
41+
continue;
42+
}
3843
if (is_team_lead()) {
3944
nodeA = nodeA_view(blockid);
4045
nodeB = nodeB_view(blockid);

include/mra/kernels/multiply.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ namespace mra {
104104
size_type N,
105105
size_type K)
106106
{
107-
SHARED DenseTensorView<T, NDIM> nodeA, nodeB, nodeR, cnodesR, cnodesD;
107+
SHARED DenseTensorView<const T, NDIM> nodeA, nodeB;
108+
SHARED DenseTensorView<T, NDIM> nodeR, cnodesR, cnodesD;
108109
SHARED DenseTensorView<T, NDIM+1> cnodesA, cnodesB, r1;
109110
SHARED T* workspace;
110111
size_type blockId = blockIdx.x;
@@ -122,6 +123,10 @@ namespace mra {
122123
}
123124

124125
for (size_type fnid = blockId; fnid < N; fnid += gridDim.x){
126+
if (nodeR_view.is_zero(fnid)) {
127+
/* no work to do */
128+
continue;
129+
}
125130
if (is_team_lead()) {
126131
nodeA = nodeA_view(fnid);
127132
nodeB = nodeB_view(fnid);

include/mra/kernels/norm.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,16 @@ namespace mra {
3737
size_type K,
3838
const Key<NDIM>& key)
3939
{
40-
const bool is_t0 = (0 == thread_id());
4140
const size_type TWOK2NDIM = std::pow(2*K, NDIM);
42-
SHARED DenseTensorView<T, NDIM> n;
41+
SHARED DenseTensorView<const T, NDIM> n;
4342
SHARED std::array<T, Key<NDIM>::num_children()> block_child_norms;
4443
for (size_type blockid = blockIdx.x; blockid < N; blockid += gridDim.x) {
45-
if (is_t0) {
44+
if (node.is_zero(blockid)) {
45+
/* no work to do */
46+
if (is_team_lead()) result_norms[blockid] = T(0.0);
47+
continue;
48+
}
49+
if (is_team_lead()) {
4650
n = node(blockid);
4751
for (size_type i = 0; i < Key<NDIM>::num_children(); ++i) {
4852
block_child_norms[i] = (child_norms[i] != nullptr) ? child_norms[i][blockid] : T(0.0);

include/mra/kernels/reconstruct.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace mra {
6161
Key<NDIM> key,
6262
size_type N,
6363
size_type K,
64-
concepts::TensorView<NDIM+1> auto node_view,
64+
const concepts::TensorView<NDIM+1> auto node_view,
6565
T* tmp_ptr,
6666
const concepts::TensorView<2> auto hg,
6767
const concepts::TensorView<NDIM+1> auto from_parent_view,
@@ -73,7 +73,8 @@ namespace mra {
7373
SHARED std::array<decltype(r_arr[0](0)), Key<NDIM>::num_children()> block_r_arr;
7474
SHARED DenseTensorView<T, NDIM> s, tmp_node;
7575
SHARED T* workspace;
76-
SHARED DenseTensorView<T, NDIM> node, from_parent;
76+
SHARED DenseTensorView<const T, NDIM> node;
77+
SHARED DenseTensorView<const T, NDIM> from_parent;
7778

7879
size_type blockId = blockIdx.x;
7980
T* block_tmp_ptr = &tmp_ptr[blockId*reconstruct_tmp_size<NDIM>(K)];
@@ -84,7 +85,13 @@ namespace mra {
8485
workspace = &block_tmp_ptr[2*TWOK2NDIM];
8586
}
8687

88+
assert(node_view.is_any_nonzero() || from_parent.is_any_nonzero() && "why did we even get here?!");
89+
8790
for (size_type fnid = blockId; fnid < N; fnid += gridDim.x){
91+
if (node.is_zero(fnid) && from_parent.is_zero(fnid)) {
92+
/* no work to do */
93+
continue;
94+
}
8895
if (is_t0) {
8996
node = node_view(fnid);
9097
from_parent = from_parent_view(fnid);
@@ -103,7 +110,7 @@ namespace mra {
103110
const Key<NDIM>& key,
104111
size_type N,
105112
size_type K,
106-
concepts::TensorView<NDIM+1> auto& node,
113+
const concepts::TensorView<NDIM+1> auto& node,
107114
const concepts::TensorView<2> auto& hg,
108115
const concepts::TensorView<NDIM+1> auto& from_parent,
109116
const concepts::TensorViewArray<NDIM+1, mra::Key<NDIM>::num_children()> auto& r_arr,
@@ -125,7 +132,7 @@ namespace mra {
125132
const Key<3>& key,
126133
size_type N,
127134
size_type K,
128-
SparseTensorView<double, 3+1>& node,
135+
const SparseTensorView<double, 3+1>& node,
129136
const SparseTensorView<double, 2>& hg,
130137
const SparseTensorView<double, 3+1>& from_parent,
131138
const std::array<SparseTensorView<double, 3+1>, mra::Key<3>::num_children()>& r_arr,

include/mra/tasks/compress.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "mra/misc/options.h"
1010
#include "mra/misc/functiondata.h"
1111
#include "mra/misc/functionset.h"
12+
#include "mra/tensor/sparsitymanager.h"
1213
#include "mra/tensor/tensor.h"
1314
#include "mra/tensor/tensorview.h"
1415
#include "mra/tensor/functionnode.h"
@@ -87,22 +88,29 @@ namespace mra
8788

8889
/* some inputs are on the device so submit a kernel */
8990

91+
SparsityInfo sparsity(N);
92+
sparsity.nonzero_if_any(in0, in1, in2, in3, in4, in5, in6, in7);
93+
//std::cout << name << " " << key << " sparsity: " << sparsity << std::endl;
94+
9095
// allocate the result
91-
result = mra::FunctionsCompressedNode<T, NDIM>(key, N, K, ttg::scope::Allocate);
92-
auto& d = result.coeffs();
96+
result.allocate(sparsity, K, ttg::scope::Allocate);
97+
9398
// Collect child leaf info
9499
mra::apply_leaf_info(result, in0, in1, in2, in3, in4, in5, in6, in7);
95-
p = mra::FunctionsReconstructedNode<T, NDIM>(key, N, K, ttg::scope::Allocate);
100+
p.allocate(sparsity, K, ttg::scope::Allocate);
96101
p.set_all_leaf(false);
97102
assert(p.is_all_leaf() == false);
98103
FunctionNorms<T, NDIM> norms(name, in0, in1, in2, in3, in4, in5, in6, in7, result);
99104

105+
100106
const std::size_t tmp_size = compress_tmp_size<NDIM>(K)*N;
101107
ttg::Buffer<T, DeviceAllocator<T>> tmp_scratch(tmp_size, TempScope);
102108
const auto& hgT = functiondata.get_hgT();
103109
/* stores sumsq for each child and for result at the end of the kernel */
104110
auto d_sumsq = ttg::Buffer<T, DeviceAllocator<T>>(N, TempScope);
105111

112+
auto& d = result.coeffs();
113+
106114
#ifndef MRA_ENABLE_HOST
107115
auto input = ttg::device::Input(p.coeffs().buffer(), d.buffer(), hgT.buffer(),
108116
tmp_scratch, d_sumsq);
@@ -131,6 +139,9 @@ namespace mra
131139
auto input_views = std::array{in0.coeffs().current_view(), in1.coeffs().current_view(), in2.coeffs().current_view(), in3.coeffs().current_view(),
132140
in4.coeffs().current_view(), in5.coeffs().current_view(), in6.coeffs().current_view(), in7.coeffs().current_view()};
133141

142+
auto sparseman = make_sparsity_manager(d, p);
143+
sparseman.populate_device_sparsity();
144+
134145
auto coeffs_view = p.coeffs().current_view();
135146
auto rcoeffs_view = d.current_view();
136147
auto hgT_view = hgT.current_view();
@@ -176,11 +187,11 @@ namespace mra
176187
for (std::size_t i = 0; i < N; ++i) {
177188
if (std::abs(p.sum(i) - 1.0) > 1e-12) {
178189
all_correct = false;
179-
std::cout << "At root of compressed tree " << key.batch() << " fn " << i << ": total normsq is " << p.sum(i) << std::endl;
190+
std::cout << name << ": at root of compressed tree " << key.batch() << " fn " << i << ": total normsq is " << p.sum(i) << std::endl;
180191
}
181192
}
182193
if (all_correct) {
183-
std::cout << "At root of compressed tree " << key.batch() << ": all norms are 1.0 with 1e-12 tolerance" << std::endl;
194+
std::cout << name << ": at root of compressed tree " << key.batch() << ": all norms are 1.0 with 1e-12 tolerance" << std::endl;
184195
}
185196
#ifndef MRA_ENABLE_HOST
186197
co_await ttg::device::forward(

include/mra/tasks/derivative.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,11 @@ namespace mra{
232232
* We can finally compute the derivative.
233233
*/
234234
if ((!left.empty() || key.is_left_boundary(axis)) && (!right.empty() || key.is_right_boundary(axis))){
235-
mra::FunctionsReconstructedNode<T, NDIM> result(key, N, K, ttg::scope::Allocate);
235+
236+
SparsityInfo sparsity(N);
237+
sparsity.nonzero_if_any(left, center, right);
238+
239+
mra::FunctionsReconstructedNode<T, NDIM> result(key, sparsity, K, ttg::scope::Allocate);
236240
result.set_all_leaf(true);
237241
auto tmp = ttg::Buffer<T>(derivative_tmp_size<NDIM>(K)*N, TempScope);
238242
const DenseTensor<T, 2+1>& operators = functiondata.get_operators();
@@ -261,6 +265,9 @@ namespace mra{
261265
co_await ttg::device::select(input);
262266
#endif // MRA_ENABLE_HOST
263267

268+
SparsityManager sparseman(result);
269+
sparseman.populate_device_sparsity();
270+
264271
auto& D = *db.current_device_ptr();
265272
auto result_view = result.coeffs().current_view();
266273
submit_derivative_kernel(D, key, left.key(), center.key(), right.key(), left.coeffs().current_view(),

include/mra/tasks/gaxpy.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,16 @@ namespace mra{
7373
send_out(t1);
7474
} else {
7575

76-
auto out = mra::FunctionsCompressedNode<T, NDIM>(key, N, K, ttg::scope::Allocate);
76+
77+
SparsityInfo sparsity(N);
78+
sparsity.nonzero_if_all(t1, t2);
79+
80+
auto out = mra::FunctionsCompressedNode<T, NDIM>(key, sparsity, K, ttg::scope::Allocate);
7781

7882
/* adapt the leaf information of the result: if the children of both nodes are leafs then
7983
* the children of the output node are leafs as well. */
8084
mra::apply_leaf_info(out, t1, t2);
8185
//std::cout << name << " " << key << " all leafs " << out.is_all_child_leaf() << std::endl;
82-
#if 0
83-
for (size_type i = 0; i < N; ++i) {
84-
for (auto child : children(key)) {
85-
auto childidx = child.childindex();
86-
out.is_child_leaf(i)[childidx] = t1.is_child_leaf(i)[childidx] && t2.is_child_leaf(i)[childidx];
87-
}
88-
}
89-
#endif // 0
9086

9187
auto norms = FunctionNorms(name, out, t1, t2);
9288

@@ -105,6 +101,9 @@ namespace mra{
105101
auto t2_view = t2.coeffs().current_view();
106102
auto out_view = out.coeffs().current_view();
107103

104+
auto sparseman = make_sparsity_manager(out);
105+
sparseman.populate_device_sparsity();
106+
108107
submit_gaxpy_kernel(key, t1_view, t2_view, out_view,
109108
scalarA, scalarB, N, K, ttg::device::current_stream());
110109

0 commit comments

Comments
 (0)