Skip to content

Commit 5f984f6

Browse files
committed
adding pack_to_host
Signed-off-by: niranda perera <niranda.perera@gmail.com>
1 parent 856f515 commit 5f984f6

6 files changed

Lines changed: 246 additions & 83 deletions

File tree

cpp/include/rapidsmpf/integrations/cudf/partition.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,36 @@ PackedData chunked_pack(
242242
cudf::table_view const& table, Buffer& bounce_buf, MemoryReservation& data_res
243243
);
244244

245+
/// @brief The minimum buffer size for `cudf::chunked_pack`.
246+
constexpr size_t cudf_chunked_pack_min_buffer_size = 1 << 20; ///< 1 MiB
247+
248+
/**
249+
* @brief Pack a table to host memory using `cudf::pack` or `cudf::chunked_pack`.
250+
*
251+
* If device memory reservation can be made for the estimated table size, `cudf::pack`
252+
* is used. Otherwise, `cudf::chunked_pack` is used with a bounce buffer size of the
253+
* estimated table size * @p cpack_buf_size_factor (with at least
254+
* `cudf_chunked_pack_min_buffer_size`).
255+
*
256+
* @param table The table to pack.
257+
* @param stream CUDA stream used for device memory operations and kernel launches.
258+
* @param host_data_res Memory reservation for the host data buffer.
259+
* @param cpack_buf_size_factor The factor to use for the chunked pack buffer size.
260+
* Default is 0.1, i.e. 10% of the estimated table size.
261+
* @param cpack_buf_mem_types The memory types to use for the bounce buffer. Default is
262+
* `DEVICE_ACCESSIBLE_MEMORY_TYPES`.
263+
*
264+
* @return A `PackedData` containing the packed table.
265+
*
266+
* @throws std::invalid_argument If the memory reservation is not host accessible.
267+
* @throws std::runtime_error If the memory reservation fails.
268+
*/
269+
std::unique_ptr<PackedData> pack_to_host(
270+
cudf::table_view const& table,
271+
rmm::cuda_stream_view stream,
272+
MemoryReservation& host_data_res,
273+
float cpack_buf_size_factor = 0.1,
274+
std::span<MemoryType const> cpack_buf_mem_types = DEVICE_ACCESSIBLE_MEMORY_TYPES
275+
);
276+
245277
} // namespace rapidsmpf

cpp/include/rapidsmpf/memory/buffer_resource.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ class BufferResource {
211211
return std::move(res);
212212
}
213213
}
214-
RAPIDSMPF_FAIL("failed to reserve memory", std::runtime_error);
214+
RAPIDSMPF_FAIL(
215+
"failed to reserve memory " + std::to_string(size), std::runtime_error
216+
);
215217
}
216218

217219
/**

cpp/include/rapidsmpf/memory/memory_type.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ constexpr std::array<MemoryType, 2> SPILL_TARGET_MEMORY_TYPES{
4040
{MemoryType::PINNED_HOST, MemoryType::HOST}
4141
};
4242

43+
/**
44+
* @brief Memory types that are device accessible in the order of preference.
45+
*/
46+
constexpr std::array<MemoryType, 2> DEVICE_ACCESSIBLE_MEMORY_TYPES{
47+
{MemoryType::DEVICE, MemoryType::PINNED_HOST}
48+
};
49+
50+
/**
51+
* @brief Memory types that are host accessible in the order of preference.
52+
*/
53+
constexpr std::array<MemoryType, 2> HOST_ACCESSIBLE_MEMORY_TYPES{
54+
{MemoryType::PINNED_HOST, MemoryType::HOST}
55+
};
56+
4357
/**
4458
* @brief Get the memory types with preference lower than or equal to @p mem_type.
4559
*
@@ -65,6 +79,28 @@ static_assert(std::ranges::equal(
6579
leq_memory_types(static_cast<MemoryType>(-1)), std::ranges::empty_view<MemoryType>{}
6680
));
6781

82+
/**
83+
* @brief Check if the memory type is host accessible.
84+
*
85+
* @param mem_type The memory type.
86+
* @return True if the memory type is host accessible, false otherwise.
87+
*/
88+
constexpr bool is_host_accessible(MemoryType mem_type) noexcept {
89+
return std::ranges::find(HOST_ACCESSIBLE_MEMORY_TYPES, mem_type)
90+
!= std::ranges::end(HOST_ACCESSIBLE_MEMORY_TYPES);
91+
};
92+
93+
/**
94+
* @brief Check if the memory type is device accessible.
95+
*
96+
* @param mem_type The memory type.
97+
* @return True if the memory type is device accessible, false otherwise.
98+
*/
99+
constexpr bool is_device_accessible(MemoryType mem_type) noexcept {
100+
return std::ranges::find(DEVICE_ACCESSIBLE_MEMORY_TYPES, mem_type)
101+
!= std::ranges::end(DEVICE_ACCESSIBLE_MEMORY_TYPES);
102+
};
103+
68104
/**
69105
* @brief Get the name of a MemoryType.
70106
*

cpp/src/integrations/cudf/partition.cpp

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,11 @@ PackedData chunked_pack(
272272
cudf::table_view const& table, Buffer& bounce_buf, MemoryReservation& data_res
273273
) {
274274
RAPIDSMPF_EXPECTS(
275-
bounce_buf.mem_type() == MemoryType::DEVICE,
276-
"bounce buffer must be in device memory",
275+
is_device_accessible(bounce_buf.mem_type()),
276+
"bounce buffer is not device accessible",
277277
std::invalid_argument
278278
);
279+
279280
// all copies will be done on the bounce buffer's stream
280281
auto stream = bounce_buf.stream();
281282
auto* br = data_res.br();
@@ -316,4 +317,70 @@ PackedData chunked_pack(
316317
return {packer.build_metadata(), std::move(data_buf)};
317318
}
318319

320+
std::unique_ptr<PackedData> pack_to_host(
321+
cudf::table_view const& table,
322+
rmm::cuda_stream_view stream,
323+
MemoryReservation& host_data_res,
324+
float chunked_pack_buffer_size_factor,
325+
std::span<MemoryType const> cpack_buf_mem_types
326+
) {
327+
RAPIDSMPF_EXPECTS(
328+
is_host_accessible(host_data_res.mem_type()),
329+
"memory reservation is not host accessible",
330+
std::invalid_argument
331+
);
332+
333+
auto* br = host_data_res.br();
334+
335+
size_t est_table_size = estimated_memory_usage(table, stream);
336+
{
337+
// make a device reservation for packing
338+
auto [pack_res, overbooking] =
339+
br->reserve(MemoryType::DEVICE, est_table_size, true);
340+
341+
if (overbooking == 0) {
342+
// if there is enough memory to pack the table, use `cudf::pack`
343+
auto packed_columns = cudf::pack(table, stream, br->device_mr());
344+
// clear the reservation as we are done with it.
345+
pack_res.clear();
346+
347+
// note that this is a device buffer, so we need to move it to host memory
348+
auto packed_data = std::make_unique<PackedData>(
349+
std::move(packed_columns.metadata),
350+
br->move(std::move(packed_columns.gpu_data), stream)
351+
);
352+
353+
// Handle the case where `cudf::pack` allocates slightly more than
354+
// the input size. This can occur because cudf uses aligned
355+
// allocations, which may exceed the requested size. To
356+
// accommodate this, we allow some wiggle room.
357+
if (packed_data->data->size > host_data_res.size()) {
358+
if (packed_data->data->size
359+
<= host_data_res.size() + total_packing_wiggle_room(table))
360+
{
361+
host_data_res =
362+
br->reserve(
363+
host_data_res.mem_type(), packed_data->data->size, true
364+
)
365+
.first;
366+
}
367+
}
368+
369+
// finally copy the packed data device buffer to HOST memory
370+
packed_data->data = br->move(std::move(packed_data->data), host_data_res);
371+
return packed_data;
372+
}
373+
}
374+
375+
// there is not enough memory to use cudf::pack. Use chunked_pack.
376+
auto chunk_size = std::max(
377+
static_cast<size_t>(est_table_size * chunked_pack_buffer_size_factor),
378+
cudf_chunked_pack_min_buffer_size
379+
);
380+
auto bounce_res = br->reserve_or_fail(chunk_size, cpack_buf_mem_types);
381+
auto bounce_buf = br->allocate(chunk_size, stream, bounce_res);
382+
383+
return std::make_unique<PackedData>(chunked_pack(table, *bounce_buf, host_data_res));
384+
}
385+
319386
} // namespace rapidsmpf

cpp/src/streaming/cudf/table_chunk.cpp

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -171,57 +171,7 @@ TableChunk TableChunk::copy(MemoryReservation& reservation) const {
171171
// serialize `table_view()` into a packed_columns and then we move
172172
// the packed_columns' gpu_data to a new host buffer.
173173

174-
// make a reservation for packing
175-
auto [pack_res, overbooking] = br->reserve(
176-
MemoryType::DEVICE,
177-
estimated_memory_usage(table_view(), stream()),
178-
true
179-
);
180-
181-
if (overbooking > 0) {
182-
// there is not enough memory to pack the table.
183-
size_t avail_dev_mem = pack_res.size() - overbooking;
184-
RAPIDSMPF_EXPECTS(
185-
avail_dev_mem > 1 << 20,
186-
"not enough device memory for the bounce buffer",
187-
std::runtime_error
188-
);
189-
auto bounce_buf = br->allocate(avail_dev_mem, stream(), pack_res);
190-
191-
packed_data = std::make_unique<PackedData>(
192-
chunked_pack(table_view(), *bounce_buf, reservation)
193-
);
194-
} else {
195-
// if there is enough memory to pack the table, use `cudf::pack`
196-
auto packed_columns =
197-
cudf::pack(table_view(), stream(), br->device_mr());
198-
// clear the reservation as we are done with it.
199-
pack_res.clear();
200-
packed_data = std::make_unique<PackedData>(
201-
std::move(packed_columns.metadata),
202-
br->move(std::move(packed_columns.gpu_data), stream())
203-
);
204-
205-
// Handle the case where `cudf::pack` allocates slightly more than
206-
// the input size. This can occur because cudf uses aligned
207-
// allocations, which may exceed the requested size. To
208-
// accommodate this, we allow some wiggle room.
209-
if (packed_data->data->size > reservation.size()) {
210-
if (packed_data->data->size
211-
<= reservation.size()
212-
+ total_packing_wiggle_room(table_view()))
213-
{
214-
reservation =
215-
br->reserve(
216-
MemoryType::HOST, packed_data->data->size, true
217-
)
218-
.first;
219-
}
220-
}
221-
// finally copy the packed data device buffer to HOST memory
222-
packed_data->data =
223-
br->move(std::move(packed_data->data), reservation);
224-
}
174+
packed_data = pack_to_host(table_view(), stream(), reservation);
225175
}
226176
return TableChunk(std::move(packed_data));
227177
}

0 commit comments

Comments
 (0)