Skip to content

[C++][GPU][FlightRPC] CudaMemoryManager::ViewBufferFrom() creates non-CudaBuffer views on HMM-enabled systems #55360

Open

Description

@kou

Describe the bug, including details regarding any error messages, version, and platform.

GrpcCudaDataTest.TestDoGet, GrpcCudaDataTest.TestDoPut and GrpcCudaDataTest.TestDoExchange in arrow-flight-test fail on a system where HMM (Heterogeneous Memory Management) is enabled:

[ RUN      ] GrpcCudaDataTest.TestDoGet
unknown file: Failure
C++ exception with description "std::bad_cast" thrown in the test body.

[  FAILED  ] GrpcCudaDataTest.TestDoGet (125 ms)
[ RUN      ] GrpcCudaDataTest.TestDoPut
.../cpp/src/arrow/flight/test_definitions.cc:1368: Failure
Failed
'do_put_result.writer->WriteRecordBatch(*cuda_batch)' failed with Unknown error: Flight RPC failed with message: Unexpected error in RPC handling. ...
[  FAILED  ] GrpcCudaDataTest.TestDoPut (7 ms)
[ RUN      ] GrpcCudaDataTest.TestDoExchange
.../cpp/src/arrow/flight/test_definitions.cc:1408: Failure
Failed
'_error_or_value132.status()' failed with Unknown error: Flight RPC failed with message: Unexpected error in RPC handling. ...
[  FAILED  ] GrpcCudaDataTest.TestDoExchange (4 ms)

Backtrace of the std::bad_cast in TestDoGet:

#0  __cxa_throw ()
#1  arrow::cuda::CudaBufferReader::CudaBufferReader(std::shared_ptr<arrow::Buffer> const&)
#2  arrow::cuda::CudaMemoryManager::GetBufferReader(std::shared_ptr<arrow::Buffer>)
#3  arrow::Buffer::GetReader(std::shared_ptr<arrow::Buffer>)
#4  arrow::ipc::StreamDecoderInternal::OnRecordBatchMessageDecoded(...)
...
#8  arrow::flight::ClientStreamReader::Next()
#9  arrow::flight::CudaDataTest::TestDoGet()

CudaMemoryManager::ViewBufferFrom() creates a CPU-on-device view when CudaContext::GetDeviceAddress() succeeds:

Result<std::shared_ptr<Buffer>> CudaMemoryManager::ViewBufferFrom(
const std::shared_ptr<Buffer>& buf, const std::shared_ptr<MemoryManager>& from) {
if (from->is_cpu()) {
// CPU-on-device view
ARROW_ASSIGN_OR_RAISE(auto to_context, cuda_device()->GetContext());
ARROW_ASSIGN_OR_RAISE(auto address, GetDeviceAddress(buf->data(), to_context));
return std::make_shared<Buffer>(address, buf->size(), shared_from_this(), buf);
}
return nullptr;
}

CudaContext::GetDeviceAddress() uses cuPointerGetAttribute(CU_POINTER_ATTRIBUTE_DEVICE_POINTER):

Result<uintptr_t> CudaContext::GetDeviceAddress(uintptr_t addr) {
ContextSaver set_temporary(*this);
CUdeviceptr ptr;
CU_RETURN_NOT_OK("cuPointerGetAttribute",
cuPointerGetAttribute(&ptr, CU_POINTER_ATTRIBUTE_DEVICE_POINTER,
static_cast<CUdeviceptr>(addr)));
return static_cast<uintptr_t>(ptr);
}

The view is a plain arrow::Buffer on the CUDA device, not a CudaBuffer.

Without HMM, cuPointerGetAttribute() succeeds only for page-locked host memory registered to CUDA. With HMM, it also succeeds for pageable host memory allocated by malloc() and so on. So Buffer::ViewOrCopy() returns a view instead of a copy for normal CPU buffers.

Flight client uses Buffer::ViewOrCopy(data->body, memory_manager_). The result is passed to CudaBufferReader via Buffer::GetReader(). CudaBufferReader requires a CudaBuffer. So it throws std::bad_cast:

CudaBufferReader::CudaBufferReader(const std::shared_ptr<Buffer>& buffer)
: address_(buffer->address()), size_(buffer->size()), position_(0), is_open_(true) {
auto maybe_buffer = CudaBuffer::FromBuffer(buffer);
if (ARROW_PREDICT_FALSE(!maybe_buffer.ok())) {
throw std::bad_cast();
}
buffer_ = *std::move(maybe_buffer);
context_ = buffer_->context();
}

Component(s)

C++, GPU, FlightRPC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions