Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_Details_packCrsMatrix_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Tpetra: Templated Linear Algebra Services Package
4//
5// Copyright 2008 NTESS and the Tpetra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef TPETRA_DETAILS_PACKCRSMATRIX_DEF_HPP
11#define TPETRA_DETAILS_PACKCRSMATRIX_DEF_HPP
12
13#include "TpetraCore_config.h"
14#include "Teuchos_Array.hpp"
15#include "Teuchos_ArrayView.hpp"
24#include <memory>
25#include <sstream>
26#include <stdexcept>
27#include <string>
28
51
52namespace Tpetra {
53
54//
55// Users must never rely on anything in the Details namespace.
56//
57namespace Details {
58
59namespace PackCrsMatrixImpl {
67template <class OutputOffsetsViewType,
68 class CountsViewType,
69 class InputOffsetsViewType,
70 class InputLocalRowIndicesViewType,
71 class InputLocalRowPidsViewType,
72 const bool debug =
73#ifdef HAVE_TPETRA_DEBUG
74 true
75#else
76 false
77#endif // HAVE_TPETRA_DEBUG
78 >
79class NumPacketsAndOffsetsFunctor {
80 public:
81 typedef typename OutputOffsetsViewType::non_const_value_type output_offset_type;
82 typedef typename CountsViewType::non_const_value_type count_type;
83 typedef typename InputOffsetsViewType::non_const_value_type input_offset_type;
84 typedef typename InputLocalRowIndicesViewType::non_const_value_type local_row_index_type;
85 typedef typename InputLocalRowPidsViewType::non_const_value_type local_row_pid_type;
86 // output Views drive where execution happens.
87 typedef typename OutputOffsetsViewType::device_type device_type;
88 static_assert(std::is_same<typename CountsViewType::device_type::execution_space,
89 typename device_type::execution_space>::value,
90 "OutputOffsetsViewType and CountsViewType must have the same execution space.");
91 static_assert(Kokkos::is_view<OutputOffsetsViewType>::value,
92 "OutputOffsetsViewType must be a Kokkos::View.");
93 static_assert(std::is_same<typename OutputOffsetsViewType::value_type, output_offset_type>::value,
94 "OutputOffsetsViewType must be a nonconst Kokkos::View.");
95 static_assert(std::is_integral<output_offset_type>::value,
96 "The type of each entry of OutputOffsetsViewType must be a built-in integer type.");
97 static_assert(Kokkos::is_view<CountsViewType>::value,
98 "CountsViewType must be a Kokkos::View.");
99 static_assert(std::is_same<typename CountsViewType::value_type, output_offset_type>::value,
100 "CountsViewType must be a nonconst Kokkos::View.");
101 static_assert(std::is_integral<count_type>::value,
102 "The type of each entry of CountsViewType must be a built-in integer type.");
103 static_assert(Kokkos::is_view<InputOffsetsViewType>::value,
104 "InputOffsetsViewType must be a Kokkos::View.");
105 static_assert(std::is_integral<input_offset_type>::value,
106 "The type of each entry of InputOffsetsViewType must be a built-in integer type.");
107 static_assert(Kokkos::is_view<InputLocalRowIndicesViewType>::value,
108 "InputLocalRowIndicesViewType must be a Kokkos::View.");
109 static_assert(std::is_integral<local_row_index_type>::value,
110 "The type of each entry of InputLocalRowIndicesViewType must be a built-in integer type.");
111
112 NumPacketsAndOffsetsFunctor(const OutputOffsetsViewType& outputOffsets,
113 const CountsViewType& counts,
114 const InputOffsetsViewType& rowOffsets,
115 const InputLocalRowIndicesViewType& lclRowInds,
116 const InputLocalRowPidsViewType& lclRowPids,
117 const count_type sizeOfLclCount,
118 const count_type sizeOfGblColInd,
119 const count_type sizeOfPid,
120 const count_type sizeOfValue)
121 : outputOffsets_(outputOffsets)
122 , counts_(counts)
123 , rowOffsets_(rowOffsets)
124 , lclRowInds_(lclRowInds)
125 , lclRowPids_(lclRowPids)
126 , sizeOfLclCount_(sizeOfLclCount)
127 , sizeOfGblColInd_(sizeOfGblColInd)
128 , sizeOfPid_(sizeOfPid)
129 , sizeOfValue_(sizeOfValue)
130 , error_("error") // don't forget this, or you'll get segfaults!
131 {
132 if (debug) {
133 const size_t numRowsToPack = static_cast<size_t>(lclRowInds_.extent(0));
134
135 if (numRowsToPack != static_cast<size_t>(counts_.extent(0))) {
136 std::ostringstream os;
137 os << "lclRowInds.extent(0) = " << numRowsToPack
138 << " != counts.extent(0) = " << counts_.extent(0)
139 << ".";
140 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, os.str());
141 }
142 if (static_cast<size_t>(numRowsToPack + 1) !=
143 static_cast<size_t>(outputOffsets_.extent(0))) {
144 std::ostringstream os;
145 os << "lclRowInds.extent(0) + 1 = " << (numRowsToPack + 1)
146 << " != outputOffsets.extent(0) = " << outputOffsets_.extent(0)
147 << ".";
148 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, os.str());
149 }
150 }
151 }
152
153 KOKKOS_INLINE_FUNCTION void
154 operator()(const local_row_index_type& curInd,
155 output_offset_type& update,
156 const bool final) const {
157 if (debug) {
158 if (curInd < static_cast<local_row_index_type>(0)) {
159 error_() = 1;
160 return;
161 }
162 }
163
164 if (final) {
165 if (debug) {
166 if (curInd >= static_cast<local_row_index_type>(outputOffsets_.extent(0))) {
167 error_() = 2;
168 return;
169 }
170 }
171 outputOffsets_(curInd) = update;
172 }
173
174 if (curInd < static_cast<local_row_index_type>(counts_.extent(0))) {
175 const auto lclRow = lclRowInds_(curInd);
176 if (static_cast<size_t>(lclRow + 1) >= static_cast<size_t>(rowOffsets_.extent(0)) ||
177 static_cast<local_row_index_type>(lclRow) < static_cast<local_row_index_type>(0)) {
178 error_() = 3;
179 return;
180 }
181 // count_type could differ from the type of each row offset.
182 // For example, row offsets might each be 64 bits, but if their
183 // difference always fits in 32 bits, we may then safely use a
184 // 32-bit count_type.
185 const count_type count =
186 static_cast<count_type>(rowOffsets_(lclRow + 1) - rowOffsets_(lclRow));
187
188 // We pack first the number of entries in the row, then that
189 // many global column indices, then that many pids (if any),
190 // then that many values. However, if the number of entries in
191 // the row is zero, we pack nothing.
192 const count_type numBytes = (count == 0) ? static_cast<count_type>(0) : sizeOfLclCount_ + count * (sizeOfGblColInd_ + (lclRowPids_.size() > 0 ? sizeOfPid_ : 0) + sizeOfValue_);
193
194 if (final) {
195 counts_(curInd) = numBytes;
196 }
197 update += numBytes;
198 }
199 }
200
201 // mfh 31 May 2017: Don't need init or join. If you have join, MUST
202 // have join both with and without volatile! Otherwise intrawarp
203 // joins are really slow on GPUs.
204
206 int getError() const {
207 auto error_h = Kokkos::create_mirror_view(error_);
208 // DEEP_COPY REVIEW - DEVICE-TO-HOSTMIRROR
209 // Note: In the UVM case, this would otherwise be a no-op
210 // and thus not fence, so the value might not be correct on return
211 // In the non-UVM case, create_mirror_view will block for the allocation
212 Kokkos::deep_copy(error_h, error_);
213 return error_h();
214 }
215
216 private:
217 OutputOffsetsViewType outputOffsets_;
218 CountsViewType counts_;
219 typename InputOffsetsViewType::const_type rowOffsets_;
220 typename InputLocalRowIndicesViewType::const_type lclRowInds_;
221 typename InputLocalRowPidsViewType::const_type lclRowPids_;
222 count_type sizeOfLclCount_;
223 count_type sizeOfGblColInd_;
224 count_type sizeOfPid_;
225 count_type sizeOfValue_;
226 Kokkos::View<int, device_type> error_;
227};
228
238template <class OutputOffsetsViewType,
239 class CountsViewType,
240 class InputOffsetsViewType,
241 class InputLocalRowIndicesViewType,
242 class InputLocalRowPidsViewType>
243typename CountsViewType::non_const_value_type
244computeNumPacketsAndOffsets(const OutputOffsetsViewType& outputOffsets,
245 const CountsViewType& counts,
246 const InputOffsetsViewType& rowOffsets,
247 const InputLocalRowIndicesViewType& lclRowInds,
248 const InputLocalRowPidsViewType& lclRowPids,
249 const typename CountsViewType::non_const_value_type sizeOfLclCount,
250 const typename CountsViewType::non_const_value_type sizeOfGblColInd,
251 const typename CountsViewType::non_const_value_type sizeOfPid,
252 const typename CountsViewType::non_const_value_type sizeOfValue) {
253 typedef NumPacketsAndOffsetsFunctor<OutputOffsetsViewType,
254 CountsViewType, typename InputOffsetsViewType::const_type,
255 typename InputLocalRowIndicesViewType::const_type,
256 typename InputLocalRowPidsViewType::const_type>
257 functor_type;
258 typedef typename CountsViewType::non_const_value_type count_type;
259 typedef typename OutputOffsetsViewType::size_type size_type;
260 typedef typename OutputOffsetsViewType::execution_space execution_space;
261 typedef typename functor_type::local_row_index_type LO;
262 typedef Kokkos::RangePolicy<execution_space, LO> range_type;
263 const char prefix[] = "computeNumPacketsAndOffsets: ";
264
265 count_type count = 0;
266 const count_type numRowsToPack = lclRowInds.extent(0);
267
268 if (numRowsToPack == 0) {
269 return count;
270 } else {
271 TEUCHOS_TEST_FOR_EXCEPTION(rowOffsets.extent(0) <= static_cast<size_type>(1),
272 std::invalid_argument, prefix << "There is at least one row to pack, "
273 "but the matrix has no rows. lclRowInds.extent(0) = "
274 << numRowsToPack << ", but rowOffsets.extent(0) = " << rowOffsets.extent(0) << " <= 1.");
275 TEUCHOS_TEST_FOR_EXCEPTION(outputOffsets.extent(0) !=
276 static_cast<size_type>(numRowsToPack + 1),
277 std::invalid_argument,
278 prefix << "Output dimension does not match number of rows to pack. "
279 << "outputOffsets.extent(0) = " << outputOffsets.extent(0)
280 << " != lclRowInds.extent(0) + 1 = "
281 << static_cast<size_type>(numRowsToPack + 1) << ".");
282 TEUCHOS_TEST_FOR_EXCEPTION(counts.extent(0) != numRowsToPack, std::invalid_argument,
283 prefix << "counts.extent(0) = " << counts.extent(0)
284 << " != numRowsToPack = " << numRowsToPack << ".");
285
286 functor_type f(outputOffsets, counts, rowOffsets,
287 lclRowInds, lclRowPids, sizeOfLclCount,
288 sizeOfGblColInd, sizeOfPid, sizeOfValue);
289 Kokkos::parallel_scan(range_type(0, numRowsToPack + 1), f);
290
291 // At least in debug mode, this functor checks for errors.
292 const int errCode = f.getError();
293 TEUCHOS_TEST_FOR_EXCEPTION(errCode != 0, std::runtime_error, prefix << "parallel_scan error code " << errCode << " != 0.");
294
295#if 0
296 size_t total = 0;
297 for (LO k = 0; k < numRowsToPack; ++k) {
298 total += counts[k];
299 }
300 if (outputOffsets(numRowsToPack) != total) {
301 if (errStr.get () == NULL) {
302 errStr = std::unique_ptr<std::ostringstream> (new std::ostringstream ());
303 }
304 std::ostringstream& os = *errStr;
305 os << prefix
306 << "outputOffsets(numRowsToPack=" << numRowsToPack << ") "
307 << outputOffsets(numRowsToPack) << " != sum of counts = "
308 << total << "." << std::endl;
309 if (numRowsToPack != 0) {
310 // Only print the array if it's not too long.
311 if (numRowsToPack < static_cast<LO> (10)) {
312 os << "outputOffsets: [";
313 for (LO i = 0; i <= numRowsToPack; ++i) {
314 os << outputOffsets(i);
315 if (static_cast<LO> (i + 1) <= numRowsToPack) {
316 os << ",";
317 }
318 }
319 os << "]" << std::endl;
320 os << "counts: [";
321 for (LO i = 0; i < numRowsToPack; ++i) {
322 os << counts(i);
323 if (static_cast<LO> (i + 1) < numRowsToPack) {
324 os << ",";
325 }
326 }
327 os << "]" << std::endl;
328 }
329 else {
330 os << "outputOffsets(" << (numRowsToPack-1) << ") = "
331 << outputOffsets(numRowsToPack-1) << "." << std::endl;
332 }
333 }
334 count = outputOffsets(numRowsToPack);
335 return {false, errStr};
336 }
337#endif // HAVE_TPETRA_DEBUG
338
339 // Get last entry of outputOffsets, which is the sum of the entries
340 // of counts. Don't assume UVM.
341 using Tpetra::Details::getEntryOnHost;
342 return static_cast<count_type>(getEntryOnHost(outputOffsets,
343 numRowsToPack));
344 }
345}
346
362template <class ST, class ColumnMap, class BufferDeviceType>
363KOKKOS_FUNCTION
364 Kokkos::pair<int, size_t>
365 packCrsMatrixRow(const ColumnMap& col_map,
366 const Kokkos::View<char*, BufferDeviceType>& exports,
368 const typename PackTraits<int>::input_array_type& pids_in,
369 const typename PackTraits<ST>::input_array_type& vals_in,
370 const size_t offset,
371 const size_t num_ent,
372 const size_t num_bytes_per_value,
373 const bool pack_pids) {
374 using Kokkos::subview;
375 using LO = typename ColumnMap::local_ordinal_type;
376 using GO = typename ColumnMap::global_ordinal_type;
377 using return_type = Kokkos::pair<int, size_t>;
378
379 if (num_ent == 0) {
380 // Empty rows always take zero bytes, to ensure sparsity.
381 return return_type(0, 0);
382 }
383
384 const LO num_ent_LO = static_cast<LO>(num_ent); // packValueCount wants this
385 const size_t num_ent_beg = offset;
386 const size_t num_ent_len = PackTraits<LO>::packValueCount(num_ent_LO);
387
388 const size_t gids_beg = num_ent_beg + num_ent_len;
389 const size_t gids_len = num_ent * PackTraits<GO>::packValueCount(GO(0));
390
391 const size_t pids_beg = gids_beg + gids_len;
392 const size_t pids_len = pack_pids ? num_ent * PackTraits<int>::packValueCount(int(0)) : static_cast<size_t>(0);
393
394 const size_t vals_beg = gids_beg + gids_len + pids_len;
395 const size_t vals_len = num_ent * num_bytes_per_value;
396
397 char* const num_ent_out = exports.data() + num_ent_beg;
398 char* const gids_out = exports.data() + gids_beg;
399 char* const pids_out = pack_pids ? exports.data() + pids_beg : NULL;
400 char* const vals_out = exports.data() + vals_beg;
401
402 size_t num_bytes_out = 0;
403 int error_code = 0;
404 num_bytes_out += PackTraits<LO>::packValue(num_ent_out, num_ent_LO);
405
406 {
407 // Copy column indices one at a time, so that we don't need
408 // temporary storage.
409 for (size_t k = 0; k < num_ent; ++k) {
410 const LO lid = lids_in[k];
411 const GO gid = col_map.getGlobalElement(lid);
412 num_bytes_out += PackTraits<GO>::packValue(gids_out, k, gid);
413 }
414 // Copy PIDs one at a time, so that we don't need temporary storage.
415 if (pack_pids) {
416 for (size_t k = 0; k < num_ent; ++k) {
417 const LO lid = lids_in[k];
418 const int pid = pids_in[lid];
419 num_bytes_out += PackTraits<int>::packValue(pids_out, k, pid);
420 }
421 }
422 const auto p =
423 PackTraits<ST>::packArray(vals_out, vals_in.data(), num_ent);
424 error_code += p.first;
425 num_bytes_out += p.second;
426 }
427
428 if (error_code != 0) {
429 return return_type(10, num_bytes_out);
430 }
431
432 const size_t expected_num_bytes =
433 num_ent_len + gids_len + pids_len + vals_len;
434 if (num_bytes_out != expected_num_bytes) {
435 return return_type(11, num_bytes_out);
436 }
437 return return_type(0, num_bytes_out);
438}
439
440template <class LocalMatrix, class LocalMap, class BufferDeviceType>
441struct PackCrsMatrixFunctor {
442 typedef LocalMatrix local_matrix_device_type;
443 typedef LocalMap local_map_type;
444 typedef typename local_matrix_device_type::value_type ST;
445 typedef typename local_map_type::local_ordinal_type LO;
446 typedef typename local_map_type::global_ordinal_type GO;
447 typedef typename local_matrix_device_type::device_type DT;
448
449 typedef Kokkos::View<const size_t*, BufferDeviceType>
450 num_packets_per_lid_view_type;
451 typedef Kokkos::View<const size_t*, BufferDeviceType> offsets_view_type;
452 typedef Kokkos::View<char*, BufferDeviceType> exports_view_type;
453 using export_lids_view_type = typename PackTraits<LO>::input_array_type;
454 using source_pids_view_type = typename PackTraits<int>::input_array_type;
455
456 typedef typename num_packets_per_lid_view_type::non_const_value_type
457 count_type;
458 typedef typename offsets_view_type::non_const_value_type
459 offset_type;
460 typedef Kokkos::pair<int, LO> value_type;
461
462 static_assert(std::is_same<LO, typename local_matrix_device_type::ordinal_type>::value,
463 "local_map_type::local_ordinal_type and "
464 "local_matrix_device_type::ordinal_type must be the same.");
465
466 local_matrix_device_type local_matrix;
467 local_map_type local_col_map;
468 exports_view_type exports;
469 num_packets_per_lid_view_type num_packets_per_lid;
470 export_lids_view_type export_lids;
471 source_pids_view_type source_pids;
472 offsets_view_type offsets;
473 size_t num_bytes_per_value;
474 bool pack_pids;
475
476 PackCrsMatrixFunctor(const local_matrix_device_type& local_matrix_in,
477 const local_map_type& local_col_map_in,
478 const exports_view_type& exports_in,
479 const num_packets_per_lid_view_type& num_packets_per_lid_in,
480 const export_lids_view_type& export_lids_in,
481 const source_pids_view_type& source_pids_in,
482 const offsets_view_type& offsets_in,
483 const size_t num_bytes_per_value_in,
484 const bool pack_pids_in)
485 : local_matrix(local_matrix_in)
486 , local_col_map(local_col_map_in)
487 , exports(exports_in)
488 , num_packets_per_lid(num_packets_per_lid_in)
489 , export_lids(export_lids_in)
490 , source_pids(source_pids_in)
491 , offsets(offsets_in)
492 , num_bytes_per_value(num_bytes_per_value_in)
493 , pack_pids(pack_pids_in) {
494 const LO numRows = local_matrix_in.numRows();
495 const LO rowMapDim =
496 static_cast<LO>(local_matrix.graph.row_map.extent(0));
497 TEUCHOS_TEST_FOR_EXCEPTION(numRows != 0 && rowMapDim != numRows + static_cast<LO>(1),
498 std::logic_error, "local_matrix.graph.row_map.extent(0) = " << rowMapDim << " != numRows (= " << numRows << " ) + 1.");
499 }
500
501 KOKKOS_INLINE_FUNCTION void init(value_type& dst) const {
502 using ::Tpetra::Details::OrdinalTraits;
503 dst = Kokkos::make_pair(0, OrdinalTraits<LO>::invalid());
504 }
505
506 KOKKOS_INLINE_FUNCTION void
507 join(value_type& dst, const value_type& src) const {
508 // `dst` should reflect the first (least) bad index and all other
509 // associated error codes and data, so prefer keeping it.
510 if (src.first != 0 && dst.first == 0) {
511 dst = src;
512 }
513 }
514
515 KOKKOS_INLINE_FUNCTION
516 void operator()(const LO i, value_type& dst) const {
517 const size_t offset = offsets[i];
518 const LO export_lid = export_lids[i];
519 const size_t buf_size = exports.size();
520 const size_t num_bytes = num_packets_per_lid(i);
521 const size_t num_ent =
522 static_cast<size_t>(local_matrix.graph.row_map[export_lid + 1] - local_matrix.graph.row_map[export_lid]);
523
524 // Only pack this row's data if it has a nonzero number of
525 // entries. We can do this because receiving processes get the
526 // number of packets, and will know that zero packets means zero
527 // entries.
528 if (num_ent == 0) {
529 return;
530 }
531
532 if (export_lid >= local_matrix.numRows()) {
533 if (dst.first != 0) { // keep only the first error
534 dst = Kokkos::make_pair(1, i); // invalid row
535 }
536 return;
537 } else if ((offset > buf_size || offset + num_bytes > buf_size)) {
538 if (dst.first != 0) { // keep only the first error
539 dst = Kokkos::make_pair(2, i); // out of bounds
540 }
541 return;
542 }
543
544 // We can now pack this row
545
546 // Since the matrix is locally indexed on the calling process, we
547 // have to use its column Map (which it _must_ have in this case)
548 // to convert to global indices.
549 const auto row_beg = local_matrix.graph.row_map[export_lid];
550 const auto row_end = local_matrix.graph.row_map[export_lid + 1];
551 auto vals_in = subview(local_matrix.values,
552 Kokkos::make_pair(row_beg, row_end));
553 auto lids_in = subview(local_matrix.graph.entries,
554 Kokkos::make_pair(row_beg, row_end));
555 typedef local_map_type LMT;
556 typedef BufferDeviceType BDT;
557 auto p = packCrsMatrixRow<ST, LMT, BDT>(local_col_map, exports, lids_in,
558 source_pids, vals_in, offset,
559 num_ent, num_bytes_per_value,
560 pack_pids);
561 int error_code_this_row = p.first;
562 size_t num_bytes_packed_this_row = p.second;
563 if (error_code_this_row != 0) {
564 if (dst.first != 0) { // keep only the first error
565 dst = Kokkos::make_pair(error_code_this_row, i); // bad pack
566 }
567 } else if (num_bytes_packed_this_row != num_bytes) {
568 if (dst.first != 0) { // keep only the first error
569 dst = Kokkos::make_pair(3, i);
570 }
571 }
572 }
573};
574
582template <class LocalMatrix, class LocalMap, class BufferDeviceType>
583void do_pack(const LocalMatrix& local_matrix,
584 const LocalMap& local_map,
585 const Kokkos::View<char*, BufferDeviceType>& exports,
586 const typename PackTraits<size_t>::input_array_type& num_packets_per_lid,
588 const typename PackTraits<int>::input_array_type& source_pids,
589 const Kokkos::View<const size_t*, BufferDeviceType>& offsets,
590 const size_t num_bytes_per_value,
591 const bool pack_pids) {
592 using LO = typename LocalMap::local_ordinal_type;
593 using DT = typename LocalMatrix::device_type;
594 using range_type = Kokkos::RangePolicy<typename DT::execution_space, LO>;
595 const char prefix[] = "Tpetra::Details::do_pack: ";
596
597 if (export_lids.extent(0) != 0) {
598 TEUCHOS_TEST_FOR_EXCEPTION(static_cast<size_t>(offsets.extent(0)) !=
599 static_cast<size_t>(export_lids.extent(0) + 1),
600 std::invalid_argument, prefix << "offsets.extent(0) = " << offsets.extent(0) << " != export_lids.extent(0) (= " << export_lids.extent(0) << ") + 1.");
601 TEUCHOS_TEST_FOR_EXCEPTION(export_lids.extent(0) != num_packets_per_lid.extent(0),
602 std::invalid_argument, prefix << "export_lids.extent(0) = " << export_lids.extent(0) << " != num_packets_per_lid.extent(0) = " << num_packets_per_lid.extent(0) << ".");
603 // If exports has nonzero length at this point, then the matrix
604 // has at least one entry to pack. Thus, if packing process
605 // ranks, we had better have at least one process rank to pack.
606 TEUCHOS_TEST_FOR_EXCEPTION(pack_pids && exports.extent(0) != 0 &&
607 source_pids.extent(0) == 0,
608 std::invalid_argument, prefix << "pack_pids is true, and exports.extent(0) = " << exports.extent(0) << " != 0, meaning that we need to pack at "
609 "least one matrix entry, but source_pids.extent(0) = 0.");
610 }
611
612 using pack_functor_type =
613 PackCrsMatrixFunctor<LocalMatrix, LocalMap, BufferDeviceType>;
614 pack_functor_type f(local_matrix, local_map, exports,
615 num_packets_per_lid, export_lids,
616 source_pids, offsets, num_bytes_per_value,
617 pack_pids);
618
619 typename pack_functor_type::value_type result;
620 range_type range(0, num_packets_per_lid.extent(0));
621 Kokkos::parallel_reduce(range, f, result);
622
623 if (result.first != 0) {
624 // We can't deep_copy from AnonymousSpace Views, so we can't print
625 // out any information from them in case of error.
626 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, prefix << "PackCrsMatrixFunctor "
627 "reported error code "
628 << result.first << " for the first "
629 "bad row "
630 << result.second << ".");
631 }
632}
633
665template <typename ST, typename LO, typename GO, typename NT, typename BufferDeviceType>
667 Kokkos::DualView<char*, BufferDeviceType>& exports,
668 const Kokkos::View<size_t*, BufferDeviceType>& num_packets_per_lid,
669 const Kokkos::View<const LO*, BufferDeviceType>& export_lids,
670 const Kokkos::View<const int*, typename NT::device_type>& export_pids,
671 size_t& constant_num_packets,
672 const bool pack_pids) {
673 ::Tpetra::Details::ProfilingRegion region_pack_crs_matrix(
674 "Tpetra::Details::PackCrsMatrixImpl::packCrsMatrix",
675 "Import/Export");
676 using Kokkos::View;
677 typedef BufferDeviceType DT;
678 typedef Kokkos::DualView<char*, BufferDeviceType> exports_view_type;
679 const char prefix[] = "Tpetra::Details::PackCrsMatrixImpl::packCrsMatrix: ";
680 constexpr bool debug = false;
681
682 auto local_matrix = sourceMatrix.getLocalMatrixDevice();
683 auto local_col_map = sourceMatrix.getColMap()->getLocalMap();
684
685 // Setting this to zero tells the caller to expect a possibly
686 // different ("nonconstant") number of packets per local index
687 // (i.e., a possibly different number of entries per row).
688 constant_num_packets = 0;
689
690 const size_t num_export_lids =
691 static_cast<size_t>(export_lids.extent(0));
692 TEUCHOS_TEST_FOR_EXCEPTION(num_export_lids !=
693 static_cast<size_t>(num_packets_per_lid.extent(0)),
694 std::invalid_argument, prefix << "num_export_lids.extent(0) = " << num_export_lids << " != num_packets_per_lid.extent(0) = " << num_packets_per_lid.extent(0) << ".");
695 if (num_export_lids != 0) {
696 TEUCHOS_TEST_FOR_EXCEPTION(num_packets_per_lid.data() == NULL, std::invalid_argument,
697 prefix << "num_export_lids = " << num_export_lids << " != 0, but "
698 "num_packets_per_lid.data() = "
699 << num_packets_per_lid.data() << " == NULL.");
700 }
701
702 const size_t num_bytes_per_lid = PackTraits<LO>::packValueCount(LO(0));
703 const size_t num_bytes_per_gid = PackTraits<GO>::packValueCount(GO(0));
704 const size_t num_bytes_per_pid = PackTraits<int>::packValueCount(int(0));
705
706 size_t num_bytes_per_value = 0;
708 // Assume ST is default constructible; packValueCount wants an instance.
709 num_bytes_per_value = PackTraits<ST>::packValueCount(ST());
710 } else {
711 // Since the packed data come from the source matrix, we can use
712 // the source matrix to get the number of bytes per Scalar value
713 // stored in the matrix. This assumes that all Scalar values in
714 // the source matrix require the same number of bytes. If the
715 // source matrix has no entries on the calling process, then we
716 // hope that some process does have some idea how big a Scalar
717 // value is. Of course, if no processes have any entries, then no
718 // values should be packed (though this does assume that in our
719 // packing scheme, rows with zero entries take zero bytes).
720 size_t num_bytes_per_value_l = 0;
721 if (local_matrix.values.extent(0) > 0) {
722 const ST& val = local_matrix.values(0);
723 num_bytes_per_value_l = PackTraits<ST>::packValueCount(val);
724 }
725 using Teuchos::reduceAll;
726 reduceAll<int, size_t>(*(sourceMatrix.getComm()),
727 Teuchos::REDUCE_MAX,
728 num_bytes_per_value_l,
729 Teuchos::outArg(num_bytes_per_value));
730 }
731
732 if (num_export_lids == 0) {
733 exports = exports_view_type("exports", 0);
734 return;
735 }
736
737 // Array of offsets into the pack buffer.
738 Kokkos::View<size_t*, DT> offsets("offsets", num_export_lids + 1);
739
740 // Compute number of packets per LID (row to send), as well as
741 // corresponding offsets (the prefix sum of the packet counts).
742 const size_t count =
743 computeNumPacketsAndOffsets(offsets, num_packets_per_lid,
744 local_matrix.graph.row_map, export_lids,
745 export_pids,
746 num_bytes_per_lid, num_bytes_per_gid,
747 num_bytes_per_pid, num_bytes_per_value);
748
749 // Resize the output pack buffer if needed.
750 if (count > static_cast<size_t>(exports.extent(0))) {
751 exports = exports_view_type("exports", count);
752 if (debug) {
753 std::ostringstream os;
754 os << "*** exports resized to " << count << std::endl;
755 std::cerr << os.str();
756 }
757 }
758 if (debug) {
759 std::ostringstream os;
760 os << "*** count: " << count << ", exports.extent(0): "
761 << exports.extent(0) << std::endl;
762 std::cerr << os.str();
763 }
764
765 // If exports has nonzero length at this point, then the matrix has
766 // at least one entry to pack. Thus, if packing process ranks, we
767 // had better have at least one process rank to pack.
768 TEUCHOS_TEST_FOR_EXCEPTION(pack_pids && exports.extent(0) != 0 &&
769 export_pids.extent(0) == 0,
770 std::invalid_argument, prefix << "pack_pids is true, and exports.extent(0) = " << exports.extent(0) << " != 0, meaning that we need to pack at least "
771 "one matrix entry, but export_pids.extent(0) = 0.");
772
773 typedef typename std::decay<decltype(local_matrix)>::type
775 typedef typename std::decay<decltype(local_col_map)>::type
776 local_map_type;
777
778 exports.modify_device();
779 auto exports_d = exports.view_device();
780 do_pack<local_matrix_device_type, local_map_type, DT>(local_matrix, local_col_map, exports_d, num_packets_per_lid,
781 export_lids, export_pids, offsets, num_bytes_per_value,
782 pack_pids);
783 // If we got this far, we succeeded.
784}
785
786} // namespace PackCrsMatrixImpl
787
788template <typename ST, typename LO, typename GO, typename NT>
790 Teuchos::Array<char>& exports,
791 const Teuchos::ArrayView<size_t>& numPacketsPerLID,
792 const Teuchos::ArrayView<const LO>& exportLIDs,
793 size_t& constantNumPackets) {
796 using host_exec_space = typename Kokkos::View<size_t*, device_type>::host_mirror_type::execution_space;
797 using device_exec_space = typename device_type::execution_space;
798 using host_dev_type = Kokkos::Device<host_exec_space, Kokkos::HostSpace>;
799
800 // Convert all Teuchos::Array to Kokkos::View
801
802 // This is an output array, so we don't have to copy to device here.
803 // However, we'll have to remember to copy back to host when done.
804 Kokkos::View<size_t*, buffer_device_type> num_packets_per_lid_d =
806 numPacketsPerLID.getRawPtr(),
807 numPacketsPerLID.size(), false,
808 "num_packets_per_lid");
809 // FIXME (mfh 05 Feb 2019) We should just pass the exportLIDs
810 // DualView through here, instead of recreating a device View from a
811 // host ArrayView that itself came from a DualView.
812 //
813 // This is an input array, so we have to copy to device here.
814 // However, we never need to copy it back to host.
815 Kokkos::View<const LO*, buffer_device_type> export_lids_d =
817 exportLIDs.getRawPtr(),
818 exportLIDs.size(), true,
819 "export_lids");
820
821 Kokkos::View<int*, device_type> export_pids_d; // output arg
822 Kokkos::DualView<char*, buffer_device_type> exports_dv; // output arg
823 constexpr bool pack_pids = false;
825 sourceMatrix, exports_dv, num_packets_per_lid_d, export_lids_d,
826 export_pids_d, constantNumPackets, pack_pids);
827
828 // The counts are an output of PackCrsMatrixImpl::packCrsMatrix, so we have to
829 // copy them back to host.
830 Kokkos::View<size_t*, host_dev_type> num_packets_per_lid_h(numPacketsPerLID.getRawPtr(),
831 numPacketsPerLID.size());
832 // DEEP_COPY REVIEW - DEVICE-TO-HOST
833 Kokkos::deep_copy(device_exec_space(), num_packets_per_lid_h, num_packets_per_lid_d);
834
835 // FIXME (mfh 23 Aug 2017) If we're forced to use a DualView for
836 // exports_dv above, then we have two host copies for exports_h.
837
838 // The exports are an output of PackCrsMatrixImpl::packCrsMatrix, so we have
839 // to copy them back to host.
840 if (static_cast<size_t>(exports.size()) !=
841 static_cast<size_t>(exports_dv.extent(0))) {
842 exports.resize(exports_dv.extent(0));
843 }
844 Kokkos::View<char*, host_dev_type> exports_h(exports.getRawPtr(),
845 exports.size());
846 // DEEP_COPY REVIEW - DEVICE-TO-HOST
847 Kokkos::deep_copy(device_exec_space(), exports_h, exports_dv.view_device());
848 // Fence so numPacketsPerLID and exports are safe to access by caller immediately
849 device_exec_space().fence();
850}
851
852template <typename ST, typename LO, typename GO, typename NT>
854 const CrsMatrix<ST, LO, GO, NT>& sourceMatrix,
855 Kokkos::DualView<char*, typename DistObject<char, LO, GO, NT>::buffer_device_type>& exports,
856 const Kokkos::DualView<size_t*, typename DistObject<char, LO, GO, NT>::buffer_device_type>& numPacketsPerLID,
857 const Kokkos::DualView<const LO*, typename DistObject<char, LO, GO, NT>::buffer_device_type>& exportLIDs,
858 size_t& constantNumPackets) {
861
862 // Create an empty array of PIDs, since the interface needs it.
863 Kokkos::View<int*, device_type> exportPIDs_d("exportPIDs", 0);
864 constexpr bool pack_pids = false;
865
866 // Write-only device access
867 auto numPacketsPerLID_nc = numPacketsPerLID; // const DV& -> DV
868 numPacketsPerLID_nc.clear_sync_state();
869 numPacketsPerLID_nc.modify_device();
870 auto numPacketsPerLID_d = numPacketsPerLID.view_device();
871
872 // Read-only device access
873 TEUCHOS_ASSERT(!exportLIDs.need_sync_device());
874 auto exportLIDs_d = exportLIDs.view_device();
875
876 ::Tpetra::Details::ProfilingRegion region_pack_crs_matrix_new(
877 "Tpetra::Details::packCrsMatrixNew",
878 "Import/Export");
880 sourceMatrix, exports, numPacketsPerLID_d, exportLIDs_d,
881 exportPIDs_d, constantNumPackets, pack_pids);
882}
883
884template <typename ST, typename LO, typename GO, typename NT>
886 Kokkos::DualView<char*, typename DistObject<char, LO, GO, NT>::buffer_device_type>& exports_dv,
887 const Teuchos::ArrayView<size_t>& numPacketsPerLID,
888 const Teuchos::ArrayView<const LO>& exportLIDs,
889 const Teuchos::ArrayView<const int>& sourcePIDs,
890 size_t& constantNumPackets) {
893 typedef typename Kokkos::DualView<char*, buffer_device_type>::t_host::execution_space host_exec_space;
894 typedef Kokkos::Device<host_exec_space, Kokkos::HostSpace> host_dev_type;
895
896 typename local_matrix_device_type::device_type outputDevice;
897 typedef typename NT::execution_space execution_space;
898
899 const bool verbose = ::Tpetra::Details::Behavior::verbose();
900 std::unique_ptr<std::string> prefix;
901 if (verbose) {
902 const int myRank = [&]() {
903 auto map = sourceMatrix.getMap();
904 if (map.get() == nullptr) {
905 return -1;
906 }
907 auto comm = map->getComm();
908 if (comm.get() == nullptr) {
909 return -2;
910 }
911 return comm->getRank();
912 }();
913 std::ostringstream os;
914 os << "Proc " << myRank << ": packCrsMatrixWithOwningPIDs: ";
915 prefix = std::unique_ptr<std::string>(new std::string(os.str()));
916
917 std::ostringstream os2;
918 os2 << *prefix << "start" << std::endl;
919 std::cerr << os2.str();
920 }
921
922 // Convert all Teuchos::Array to Kokkos::View
923
924 // This is an output array, so we don't have to copy to device here.
925 // However, we'll have to remember to copy back to host when done.
926 auto num_packets_per_lid_d =
928 numPacketsPerLID.getRawPtr(),
929 numPacketsPerLID.size(), false,
930 "num_packets_per_lid");
931
932 // This is an input array, so we have to copy to device here.
933 // However, we never need to copy it back to host.
934 auto export_lids_d =
936 exportLIDs.getRawPtr(),
937 exportLIDs.size(), true,
938 "export_lids");
939 // This is an input array, so we have to copy to device here.
940 // However, we never need to copy it back to host.
941 auto export_pids_d =
943 sourcePIDs.getRawPtr(),
944 sourcePIDs.size(), true,
945 "export_pids");
946 constexpr bool pack_pids = true;
947 try {
948 PackCrsMatrixImpl::packCrsMatrix(sourceMatrix, exports_dv, num_packets_per_lid_d, export_lids_d,
949 export_pids_d, constantNumPackets, pack_pids);
950 } catch (std::exception& e) {
951 if (verbose) {
952 std::ostringstream os;
953 os << *prefix << "PackCrsMatrixImpl::packCrsMatrix threw: "
954 << e.what() << std::endl;
955 std::cerr << os.str();
956 }
957 throw;
958 } catch (...) {
959 if (verbose) {
960 std::ostringstream os;
961 os << *prefix << "PackCrsMatrixImpl::packCrsMatrix threw an exception "
962 "not a subclass of std::exception"
963 << std::endl;
964 std::cerr << os.str();
965 }
966 throw;
967 }
968
969 if (numPacketsPerLID.size() != 0) {
970 try {
971 // The counts are an output of PackCrsMatrixImpl::packCrsMatrix,
972 // so we have to copy them back to host.
973 Kokkos::View<size_t*, host_dev_type> num_packets_per_lid_h(numPacketsPerLID.getRawPtr(), numPacketsPerLID.size());
974 // DEEP_COPY REVIEW - DEVICE-TO-HOST
975 Kokkos::deep_copy(execution_space(), num_packets_per_lid_h, num_packets_per_lid_d);
976 // Fence so numPacketsPerLID is safe to access by caller immediately
977 execution_space().fence();
978 } catch (std::exception& e) {
979 if (verbose) {
980 std::ostringstream os;
981 os << *prefix << "Kokkos::deep_copy threw: " << e.what() << std::endl;
982 std::cerr << os.str();
983 }
984 throw;
985 } catch (...) {
986 if (verbose) {
987 std::ostringstream os;
988 os << *prefix << "Kokkos::deep_copy threw an exception not a subclass "
989 "of std::exception"
990 << std::endl;
991 std::cerr << os.str();
992 }
993 throw;
994 }
995 }
996
997 if (verbose) {
998 std::ostringstream os;
999 os << *prefix << "done" << std::endl;
1000 std::cerr << os.str();
1001 }
1002}
1003
1004} // namespace Details
1005} // namespace Tpetra
1006
1007#define TPETRA_DETAILS_PACKCRSMATRIX_INSTANT(ST, LO, GO, NT) \
1008 template void \
1009 Details::packCrsMatrix<ST, LO, GO, NT>(const CrsMatrix<ST, LO, GO, NT>&, \
1010 Teuchos::Array<char>&, \
1011 const Teuchos::ArrayView<size_t>&, \
1012 const Teuchos::ArrayView<const LO>&, \
1013 size_t&); \
1014 template void \
1015 Details::packCrsMatrixNew<ST, LO, GO, NT>(const CrsMatrix<ST, LO, GO, NT>&, \
1016 Kokkos::DualView<char*, DistObject<char, LO, GO, NT>::buffer_device_type>&, \
1017 const Kokkos::DualView<size_t*, DistObject<char, LO, GO, NT>::buffer_device_type>&, \
1018 const Kokkos::DualView<const LO*, DistObject<char, LO, GO, NT>::buffer_device_type>&, \
1019 size_t&); \
1020 template void \
1021 Details::packCrsMatrixWithOwningPIDs<ST, LO, GO, NT>(const CrsMatrix<ST, LO, GO, NT>&, \
1022 Kokkos::DualView<char*, DistObject<char, LO, GO, NT>::buffer_device_type>&, \
1023 const Teuchos::ArrayView<size_t>&, \
1024 const Teuchos::ArrayView<const LO>&, \
1025 const Teuchos::ArrayView<const int>&, \
1026 size_t&);
1027
1028#endif // TPETRA_DETAILS_PACKCRSMATRIX_DEF_HPP
Declaration of the Tpetra::CrsMatrix class.
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Import KokkosSparse::OrdinalTraits, a traits class for "invalid" (flag) values of integer types,...
Declaration and generic definition of traits class that tells Tpetra::CrsMatrix how to pack and unpac...
Declaration of Tpetra::Details::Profiling, a scope guard for Kokkos Profiling.
Declaration and definition of Tpetra::Details::castAwayConstDualView, an implementation detail of Tpe...
Functions that wrap Kokkos::create_mirror_view, in order to avoid deep copies when not necessary,...
Declaration and definition of Tpetra::Details::getEntryOnHost.
CountsViewType::non_const_value_type computeNumPacketsAndOffsets(const OutputOffsetsViewType &outputOffsets, const CountsViewType &counts, const InputOffsetsViewType &rowOffsets, const InputLocalRowIndicesViewType &lclRowInds, const InputLocalRowPidsViewType &lclRowPids, const typename CountsViewType::non_const_value_type sizeOfLclCount, const typename CountsViewType::non_const_value_type sizeOfGblColInd, const typename CountsViewType::non_const_value_type sizeOfPid, const typename CountsViewType::non_const_value_type sizeOfValue)
Compute the number of packets and offsets for the pack procedure.
void packCrsMatrix(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Kokkos::DualView< char *, BufferDeviceType > &exports, const Kokkos::View< size_t *, BufferDeviceType > &num_packets_per_lid, const Kokkos::View< const LO *, BufferDeviceType > &export_lids, const Kokkos::View< const int *, typename NT::device_type > &export_pids, size_t &constant_num_packets, const bool pack_pids)
Pack specified entries of the given local sparse matrix for communication.
void do_pack(const LocalMatrix &local_matrix, const LocalMap &local_map, const Kokkos::View< char *, BufferDeviceType > &exports, const typename PackTraits< size_t >::input_array_type &num_packets_per_lid, const typename PackTraits< typename LocalMap::local_ordinal_type >::input_array_type &export_lids, const typename PackTraits< int >::input_array_type &source_pids, const Kokkos::View< const size_t *, BufferDeviceType > &offsets, const size_t num_bytes_per_value, const bool pack_pids)
Perform the pack operation for the matrix.
KOKKOS_FUNCTION Kokkos::pair< int, size_t > packCrsMatrixRow(const ColumnMap &col_map, const Kokkos::View< char *, BufferDeviceType > &exports, const typename PackTraits< typename ColumnMap::local_ordinal_type >::input_array_type &lids_in, const typename PackTraits< int >::input_array_type &pids_in, const typename PackTraits< ST >::input_array_type &vals_in, const size_t offset, const size_t num_ent, const size_t num_bytes_per_value, const bool pack_pids)
Packs a single row of the CrsMatrix.
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const override
The communicator over which the matrix is distributed.
typename Node::device_type device_type
The Kokkos device type.
TPETRA_DETAILS_ALWAYS_INLINE local_matrix_device_type getLocalMatrixDevice() const
The local sparse matrix.
Teuchos::RCP< const map_type > getColMap() const override
The Map that describes the column distribution in this matrix.
KokkosSparse::CrsMatrix< impl_scalar_type, local_ordinal_type, device_type, void, typename local_graph_device_type::size_type > local_matrix_device_type
The specialization of Kokkos::CrsMatrix that represents the part of the sparse matrix on each MPI pro...
static bool verbose()
Whether Tpetra is in verbose mode.
"Local" part of Map suitable for Kokkos kernels.
LocalOrdinal local_ordinal_type
The type of local indices.
GlobalOrdinal global_ordinal_type
The type of global indices.
Compute the number of packets and offsets for the pack procedure.
Kokkos::Device< typename device_type::execution_space, buffer_memory_space > buffer_device_type
Kokkos::Device specialization for communication buffers.
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
Nonmember function that computes a residual Computes R = B - A * X.
void packCrsMatrix(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Teuchos::Array< char > &exports, const Teuchos::ArrayView< size_t > &numPacketsPerLID, const Teuchos::ArrayView< const LO > &exportLIDs, size_t &constantNumPackets)
Pack specified entries of the given local sparse matrix for communication.
Impl::CreateMirrorViewFromUnmanagedHostArray< ValueType, OutputDeviceType >::output_view_type create_mirror_view_from_raw_host_array(const OutputDeviceType &, ValueType *inPtr, const size_t inSize, const bool copy=true, const char label[]="")
Variant of Kokkos::create_mirror_view that takes a raw host 1-d array as input.
void packCrsMatrixWithOwningPIDs(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Kokkos::DualView< char *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exports_dv, const Teuchos::ArrayView< size_t > &numPacketsPerLID, const Teuchos::ArrayView< const LO > &exportLIDs, const Teuchos::ArrayView< const int > &sourcePIDs, size_t &constantNumPackets)
Pack specified entries of the given local sparse matrix for communication.
void packCrsMatrixNew(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Kokkos::DualView< char *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exports, const Kokkos::DualView< size_t *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &numPacketsPerLID, const Kokkos::DualView< const LO *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exportLIDs, size_t &constantNumPackets)
Pack specified entries of the given local sparse matrix for communication, for "new" DistObject inter...
Namespace Tpetra contains the class and methods constituting the Tpetra library.
static KOKKOS_INLINE_FUNCTION Kokkos::pair< int, size_t > packArray(char outBuf[], const value_type inBuf[], const size_t numEnt)
Pack the first numEnt entries of the given input buffer of value_type, into the output buffer of byte...
static const bool compileTimeSize
Whether the number of bytes required to pack one instance of value_type is fixed at compile time.
static KOKKOS_INLINE_FUNCTION size_t packValue(char outBuf[], const T &inVal)
Pack the given value of type value_type into the given output buffer of bytes (char).
static KOKKOS_INLINE_FUNCTION size_t packValueCount(const T &)
Number of bytes required to pack or unpack the given value of type value_type.
Kokkos::View< const value_type *, Kokkos::AnonymousSpace > input_array_type
The type of an input array of value_type.