Skip to content

Commit 565f16f

Browse files
committed
arena ToT: support einsum's replicate_array path on >1 rank
einsum's special-Hadamard branch replicates the small operand via replicate_array -> make_replicated -> replicate_tensor. Two gaps broke this for an arena tensor-of-tensors on a multi-rank run: - Replicator's ctor called pmap()->local_size() purely as a reserve() hint, which asserts on a pmap that does not precompute local size (e.g. HashPmap). Skip the hint when known_local_size() is false. - replicate_tensor std::copy'd the 8-byte ArenaTensor view cells, so the replicated tile aliased the source tile's arena slab and dangled once the (temporary, post-make_replicated) source array was destroyed. Build the replicated tile as a fresh slab-backed tile and deep-copy each replicated inner cell's element data.
1 parent 1415b12 commit 565f16f

2 files changed

Lines changed: 34 additions & 2 deletions

File tree

src/TiledArray/einsum/tiledarray.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,34 @@ void replicate_tensor(Tensor &to, Tensor const &from) {
241241
// number of elements to be copied
242242
// (same as the number of elements in @c from)
243243
auto const N = from.range().volume();
244+
245+
if constexpr (TiledArray::is_arena_tensor_v<typename Tensor::value_type>) {
246+
// arena ToT: an inner cell is an 8-byte view into the outer tile's slab.
247+
// A plain std::copy of cells would leave `to` aliasing `from`'s slab --
248+
// dangling once `from` is gone. Build `to` as a fresh slab-backed tile
249+
// and deep-copy each replicated inner cell's element data.
250+
using inner_t = typename Tensor::value_type;
251+
using inner_range_t = typename inner_t::range_type;
252+
using elem_t = typename inner_t::value_type;
253+
const auto out_range = to.range();
254+
const std::size_t M = out_range.volume();
255+
auto range_fn = [&from, N](std::size_t ord) -> inner_range_t {
256+
const auto &src = from.data()[ord % N];
257+
return src.empty() ? inner_range_t{} : src.range();
258+
};
259+
to = detail::arena_outer_init<Tensor>(out_range, 1, range_fn,
260+
alignof(elem_t), /*zero_init=*/false);
261+
for (std::size_t ord = 0; ord < M; ++ord) {
262+
auto &dst = to.data()[ord];
263+
if (dst.empty()) continue;
264+
const auto &src = from.data()[ord % N];
265+
const elem_t *s = src.data();
266+
elem_t *d = dst.data();
267+
for (std::size_t k = 0; k < dst.size(); ++k) d[k] = s[k];
268+
}
269+
return;
270+
}
271+
244272
for (auto i = 0; i < to.range().volume(); i += N)
245273
std::copy(from.begin(), from.end(), to.data() + i);
246274
}

src/TiledArray/replicator.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,12 @@ class Replicator : public madness::WorldObject<Replicator<A> >,
166166
// Generate a list of local tiles from other.
167167
typename A::pmap_interface::const_iterator end = source.pmap()->end();
168168
typename A::pmap_interface::const_iterator it = source.pmap()->begin();
169-
indices_.reserve(source.pmap()->local_size());
170-
data_.reserve(source.pmap()->local_size());
169+
// local_size() is only a reserve() hint; some pmaps (e.g. HashPmap) do
170+
// not precompute it -- skip the hint rather than assert.
171+
if (source.pmap()->known_local_size()) {
172+
indices_.reserve(source.pmap()->local_size());
173+
data_.reserve(source.pmap()->local_size());
174+
}
171175
if (source.is_dense()) {
172176
// When dense, all tiles are present
173177
for (; it != end; ++it) {

0 commit comments

Comments
 (0)