Skip to content

Commit 69cd119

Browse files
committed
Enable table chunk disk copies and fix empty spill tracking
1 parent e63da37 commit 69cd119

2 files changed

Lines changed: 37 additions & 16 deletions

File tree

‎cpp/libcudf_streaming/src/table_chunk.cpp‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ table_chunk table_chunk::copy(rapidsmpf::MemoryReservation& reservation) const
150150
// into the reservation-specified memory type using libcudf:
151151
// a. DEVICE - cudf-copy table_view() into device memory.
152152
// b. PINNED_HOST - cudf::pack table_view() directly into pinned memory.
153-
// c. HOST - cudf::pack table_view() into intermediate device
154-
// memory and then copy to host memory.
153+
// c. HOST / DISK - cudf::pack table_view() into intermediate device
154+
// memory and then move to host or disk memory.
155155
//
156156
// 2. The chunk data is already packed (packed_data_ != nullptr).
157157
// Use buffer_copy() to copy the packed data into the reservation-
@@ -163,6 +163,17 @@ table_chunk table_chunk::copy(rapidsmpf::MemoryReservation& reservation) const
163163
// copy the table in device memory, or pack it to pinned/ host memory. Else, fall
164164
// through to case 2 (ie. use buffer_copy).
165165
if (is_available() && packed_data_ == nullptr) {
166+
if (data_alloc_size(rapidsmpf::MemoryType::DEVICE) == 0) {
167+
// Packing still produces the metadata needed to reconstruct the table.
168+
// Move the empty data buffer to the requested memory type without
169+
// recording a copy that transferred no bytes.
170+
auto packed_columns = cudf::pack(table_view(), stream(), br->device_mr());
171+
auto data = br->move(std::move(packed_columns.gpu_data), stream());
172+
data = br->move(std::move(data), reservation);
173+
return table_chunk(std::make_unique<rapidsmpf::PackedData>(std::move(packed_columns.metadata),
174+
std::move(data)));
175+
}
176+
166177
switch (reservation.mem_type()) {
167178
case rapidsmpf::MemoryType::DEVICE: // Case 1a.
168179
{
@@ -193,20 +204,18 @@ table_chunk table_chunk::copy(rapidsmpf::MemoryReservation& reservation) const
193204
br->release(reservation, nbytes);
194205
// The data leaves device memory here rather than through `BufferResource`, so
195206
// the spill is opened by hand and the token handed to the buffer.
196-
auto host_buffer =
197-
br->move(std::move(packed_pinned.gpu_data),
198-
stream(),
199-
// An empty table packs to a default-constructed `device_buffer`,
200-
// which ignores the resource and frees nothing, so it gets no token.
201-
nbytes > 0 ? std::make_shared<rapidsmpf::SpillTrackToken>() : nullptr);
207+
auto host_buffer = br->move(std::move(packed_pinned.gpu_data),
208+
stream(),
209+
std::make_shared<rapidsmpf::SpillTrackToken>());
202210
return table_chunk(std::make_unique<rapidsmpf::PackedData>(
203211
std::move(packed_pinned.metadata), std::move(host_buffer)));
204212
}
205213
case rapidsmpf::MemoryType::HOST: // Case 1c.
214+
case rapidsmpf::MemoryType::DISK: // Case 1c.
206215
{
207216
// We use libcudf's pack() to serialize `table_view()` into a
208217
// packed_columns and then we move the packed_columns' gpu_data to a
209-
// new host buffer.
218+
// new host/disk buffer.
210219
// TODO: use `cudf::chunked_pack()` with a bounce buffer. Currently,
211220
// `cudf::pack()` allocates device memory we haven't reserved.
212221
auto packed_columns = cudf::pack(table_view(), stream(), br->device_mr());

‎cpp/libcudf_streaming/tests/streaming/test_table_chunk.cpp‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <rapidsmpf/memory/buffer_resource.hpp>
2424
#include <rapidsmpf/owning_wrapper.hpp>
2525
#include <rapidsmpf/streaming/core/channel.hpp>
26+
#include <rapidsmpf/utils/string.hpp>
2627

2728
#include <cstdint>
2829
#include <memory>
@@ -50,12 +51,13 @@ class StreamingTableChunk : public BaseStreamingFixture,
5051
memory_limits, // memory_limits
5152
std::chrono::milliseconds{1}, // periodic_spill_check
5253
stream_pool, // stream_pool
53-
rapidsmpf::Statistics::disabled() // statistics
54-
);
54+
rapidsmpf::Statistics::disabled(), // statistics
55+
spill_dir.path());
5556
ctx = std::make_shared<rapidsmpf::streaming::Context>(
5657
options, GlobalEnvironment->comm_->logger(), br);
5758
}
5859

60+
TempDir spill_dir;
5961
cuda::stream_ref stream{cudaStream_t{cudaStreamDefault}};
6062
rmm::mr::cuda_memory_resource mr_cuda;
6163
std::shared_ptr<rapidsmpf::BufferResource> br;
@@ -172,7 +174,9 @@ TEST_F(StreamingTableChunk, FromPackedDataOnDevice)
172174

173175
INSTANTIATE_TEST_SUITE_P(StreamingTableChunkWithSpillTargets,
174176
StreamingTableChunk,
175-
::testing::ValuesIn(rapidsmpf::SPILL_TARGET_MEMORY_TYPES),
177+
::testing::ValuesIn({rapidsmpf::MemoryType::PINNED_HOST,
178+
rapidsmpf::MemoryType::HOST,
179+
rapidsmpf::MemoryType::DISK}),
176180
[](testing::TestParamInfo<rapidsmpf::MemoryType> const& info) {
177181
return std::string{rapidsmpf::to_string(info.param)};
178182
});
@@ -304,9 +308,12 @@ TEST_P(StreamingTableChunk, DeviceToHostRoundTripCopy)
304308
}
305309
}
306310

307-
// Host to host copy.
308-
auto host_res2 = br->reserve_or_fail(host_copy.data_alloc_size(spill_mem_type), spill_mem_type);
309-
auto host_copy2 = host_copy.copy(host_res2);
311+
// Disk-to-disk copies are unsupported; keep the disk chunk for the round trip.
312+
auto host_copy2 = [&] {
313+
if (spill_mem_type == rapidsmpf::MemoryType::DISK) { return std::move(host_copy); }
314+
auto host_res2 = br->reserve_or_fail(host_copy.data_alloc_size(spill_mem_type), spill_mem_type);
315+
return host_copy.copy(host_res2);
316+
}();
310317
EXPECT_FALSE(host_copy2.is_available());
311318
EXPECT_TRUE(host_copy2.is_spillable());
312319
EXPECT_EQ(host_copy2.stream().get(), stream.get());
@@ -371,7 +378,8 @@ TEST_P(StreamingTableChunk, SpillTrackingOnHostCopy)
371378
std::unordered_map<rapidsmpf::MemoryType, std::int64_t>{},
372379
std::nullopt,
373380
std::make_shared<rapidsmpf::StreamPool>(16),
374-
stats);
381+
stats,
382+
spill_dir.path());
375383

376384
auto samples = [&stats] {
377385
return stats->has_stat("buffer-spilled-time") ? stats->get_stat("buffer-spilled-time").count()
@@ -392,10 +400,14 @@ TEST_P(StreamingTableChunk, SpillTrackingOnHostCopy)
392400
// no token and must not be reported as a spill.
393401
std::ignore = round_trip(random_table_with_index(2025, 0, 0, 5));
394402
EXPECT_EQ(samples(), 0UL);
403+
auto const spill_mem_name = rapidsmpf::to_lower(rapidsmpf::to_string(spill_mem_type));
404+
EXPECT_FALSE(stats->has_stat("copy-device-to-" + spill_mem_name + "-bytes"));
405+
EXPECT_FALSE(stats->has_stat("copy-" + spill_mem_name + "-to-device-bytes"));
395406

396407
// A non-empty one does leave the device, so the round trip is recorded once.
397408
std::ignore = round_trip(random_table_with_index(2025, 64, 0, 5));
398409
EXPECT_EQ(samples(), 1UL);
410+
EXPECT_GT(stats->get_stat("copy-device-to-" + spill_mem_name + "-bytes").value(), 0);
399411
}
400412

401413
TEST_F(StreamingTableChunk, ToMessageRoundTrip)

0 commit comments

Comments
 (0)