Skip to content

Commit

Permalink
Moving synchronous HAL file APIs to the public API. (#19512)
Browse files Browse the repository at this point in the history
Implementations backed by device APIs like cuFile/DirectStorage/etc will
return false for iree_hal_file_supports_synchronous_io and the
respective HAL implementations will handle I/O themselves.
  • Loading branch information
benvanik authored Dec 18, 2024
1 parent 345b1da commit 5b67943
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 119 deletions.
53 changes: 53 additions & 0 deletions runtime/src/iree/hal/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,56 @@ IREE_API_EXPORT iree_status_t iree_hal_file_import(
IREE_TRACE_ZONE_END(z0);
return status;
}

IREE_API_EXPORT iree_hal_memory_access_t
iree_hal_file_allowed_access(iree_hal_file_t* file) {
IREE_ASSERT_ARGUMENT(file);
return _VTABLE_DISPATCH(file, allowed_access)(file);
}

IREE_API_EXPORT uint64_t iree_hal_file_length(iree_hal_file_t* file) {
IREE_ASSERT_ARGUMENT(file);
return _VTABLE_DISPATCH(file, length)(file);
}

IREE_API_EXPORT iree_hal_buffer_t* iree_hal_file_storage_buffer(
iree_hal_file_t* file) {
IREE_ASSERT_ARGUMENT(file);
return _VTABLE_DISPATCH(file, storage_buffer)(file);
}

IREE_API_EXPORT bool iree_hal_file_supports_synchronous_io(
iree_hal_file_t* file) {
IREE_ASSERT_ARGUMENT(file);
return _VTABLE_DISPATCH(file, supports_synchronous_io)(file);
}

IREE_API_EXPORT iree_status_t iree_hal_file_read(
iree_hal_file_t* file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length) {
IREE_ASSERT_ARGUMENT(file);
IREE_ASSERT_ARGUMENT(buffer);
IREE_TRACE_ZONE_BEGIN(z0);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, file_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)buffer_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)length);
iree_status_t status = _VTABLE_DISPATCH(file, read)(file, file_offset, buffer,
buffer_offset, length);
IREE_TRACE_ZONE_END(z0);
return status;
}

IREE_API_EXPORT iree_status_t iree_hal_file_write(
iree_hal_file_t* file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length) {
IREE_ASSERT_ARGUMENT(file);
IREE_ASSERT_ARGUMENT(buffer);
IREE_TRACE_ZONE_BEGIN(z0);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, file_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)buffer_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)length);
iree_status_t status = _VTABLE_DISPATCH(file, write)(
file, file_offset, buffer, buffer_offset, length);
IREE_TRACE_ZONE_END(z0);
return status;
}
56 changes: 56 additions & 0 deletions runtime/src/iree/hal/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,68 @@ IREE_API_EXPORT void iree_hal_file_retain(iree_hal_file_t* file);
// Releases the given |file| from the caller.
IREE_API_EXPORT void iree_hal_file_release(iree_hal_file_t* file);

// Returns the memory access allowed to the file.
// This may be more strict than the original file handle backing the resource
// if for example we want to prevent particular users from mutating the file.
IREE_API_EXPORT iree_hal_memory_access_t
iree_hal_file_allowed_access(iree_hal_file_t* file);

// Returns the total accessible range of the file.
// This may be a portion of the original file backing this handle.
IREE_API_EXPORT uint64_t iree_hal_file_length(iree_hal_file_t* file);

// Returns an optional device-accessible storage buffer representing the file.
// Available if the implementation is able to perform import/address-space
// mapping/etc such that device-side transfers can directly access the resources
// as if they were a normal device buffer.
IREE_API_EXPORT iree_hal_buffer_t* iree_hal_file_storage_buffer(
iree_hal_file_t* file);

// TODO(benvanik): truncate/extend? (both can be tricky with async)

// Returns true if the iree_hal_file_read and iree_hal_file_write APIs are
// available for use on the file. Not all implementations support synchronous
// I/O.
IREE_API_EXPORT bool iree_hal_file_supports_synchronous_io(
iree_hal_file_t* file);

// Synchronously reads a segment of |file| into |buffer|.
// Blocks the caller until completed. Buffers are always host mappable.
// Only available if iree_hal_file_supports_synchronous_io is true.
IREE_API_EXPORT iree_status_t iree_hal_file_read(
iree_hal_file_t* file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length);

// Synchronously writes a segment of |buffer| into |file|.
// Blocks the caller until completed. Buffers are always host mappable.
// Only available if iree_hal_file_supports_synchronous_io is true.
IREE_API_EXPORT iree_status_t iree_hal_file_write(
iree_hal_file_t* file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length);

//===----------------------------------------------------------------------===//
// iree_hal_file_t implementation details
//===----------------------------------------------------------------------===//

typedef struct iree_hal_file_vtable_t {
void(IREE_API_PTR* destroy)(iree_hal_file_t* IREE_RESTRICT file);

iree_hal_memory_access_t(IREE_API_PTR* allowed_access)(iree_hal_file_t* file);

uint64_t(IREE_API_PTR* length)(iree_hal_file_t* file);

iree_hal_buffer_t*(IREE_API_PTR* storage_buffer)(iree_hal_file_t* file);

bool(IREE_API_PTR* supports_synchronous_io)(iree_hal_file_t* file);
iree_status_t(IREE_API_PTR* read)(iree_hal_file_t* file, uint64_t file_offset,
iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset,
iree_device_size_t length);
iree_status_t(IREE_API_PTR* write)(iree_hal_file_t* file,
uint64_t file_offset,
iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset,
iree_device_size_t length);
} iree_hal_file_vtable_t;
IREE_HAL_ASSERT_VTABLE_LAYOUT(iree_hal_file_vtable_t);

Expand Down
1 change: 0 additions & 1 deletion runtime/src/iree/hal/utils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ iree_runtime_cc_library(
srcs = ["file_transfer.c"],
hdrs = ["file_transfer.h"],
deps = [
":memory_file",
"//runtime/src/iree/base",
"//runtime/src/iree/base/internal",
"//runtime/src/iree/hal",
Expand Down
1 change: 0 additions & 1 deletion runtime/src/iree/hal/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ iree_cc_library(
SRCS
"file_transfer.c"
DEPS
::memory_file
iree::base
iree::base::internal
iree::hal
Expand Down
21 changes: 18 additions & 3 deletions runtime/src/iree/hal/utils/file_transfer.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "iree/hal/utils/file_transfer.h"

#include "iree/base/internal/math.h"
#include "iree/hal/utils/memory_file.h"

//===----------------------------------------------------------------------===//
// Configuration
Expand Down Expand Up @@ -40,8 +39,6 @@
// iree_hal_transfer_operation_t
//===----------------------------------------------------------------------===//

// TODO(benvanik): move to utils/ without relying on iree_hal_memory_file_t.

// Maximum number of transfer workers that can be used; common usage should be
// 1-4 but on very large systems with lots of bandwidth we may be able to
// use more.
Expand Down Expand Up @@ -876,6 +873,15 @@ IREE_API_EXPORT iree_status_t iree_hal_device_queue_read_streaming(
target_offset, length, IREE_HAL_COPY_FLAG_NONE);
}

// This host-side transfer utility requires synchronous I/O.
// HAL implementations are expected to handle asynchronous files themselves.
if (!iree_hal_file_supports_synchronous_io(source_file)) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"provided source file does not support synchronous I/O and cannot be "
"used with streaming file transfer");
}

// Allocate full transfer operation.
iree_hal_transfer_operation_t* operation = NULL;
IREE_RETURN_IF_ERROR(iree_hal_transfer_operation_create(
Expand Down Expand Up @@ -917,6 +923,15 @@ IREE_API_EXPORT iree_status_t iree_hal_device_queue_write_streaming(
(iree_device_size_t)target_offset, length, IREE_HAL_COPY_FLAG_NONE);
}

// This host-side transfer utility requires synchronous I/O.
// HAL implementations are expected to handle asynchronous files themselves.
if (!iree_hal_file_supports_synchronous_io(target_file)) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"provided target file does not support synchronous I/O and cannot be "
"used with streaming file transfer");
}

// Allocate full transfer operation.
iree_hal_transfer_operation_t* operation = NULL;
IREE_RETURN_IF_ERROR(iree_hal_transfer_operation_create(
Expand Down
10 changes: 6 additions & 4 deletions runtime/src/iree/hal/utils/file_transfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ typedef struct iree_hal_file_transfer_options_t {
// The provided |options.loop| is used for any asynchronous host operations
// performed as part of the transfer.
//
// WARNING: this only works with memory files as created via
// iree_hal_memory_file_wrap.
// Only files that support synchronous I/O are supported. Callers must use
// iree_hal_file_supports_synchronous_io and route asynchronous files to native
// implementations.
IREE_API_EXPORT iree_status_t iree_hal_device_queue_read_streaming(
iree_hal_device_t* device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
Expand All @@ -75,8 +76,9 @@ IREE_API_EXPORT iree_status_t iree_hal_device_queue_read_streaming(
// The provided |options.loop| is used for any asynchronous host operations
// performed as part of the transfer.
//
// WARNING: this only works with memory files as created via
// iree_hal_memory_file_wrap.
// Only files that support synchronous I/O are supported. Callers must use
// iree_hal_file_supports_synchronous_io and route asynchronous files to native
// implementations.
IREE_API_EXPORT iree_status_t iree_hal_device_queue_write_streaming(
iree_hal_device_t* device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
Expand Down
108 changes: 37 additions & 71 deletions runtime/src/iree/hal/utils/memory_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,18 @@ IREE_API_EXPORT iree_status_t iree_hal_memory_file_wrap(
iree_allocator_t host_allocator, iree_hal_file_t** out_file) {
IREE_ASSERT_ARGUMENT(out_file);
*out_file = NULL;
IREE_TRACE_ZONE_BEGIN(z0);

// For now we only support host allocations but could open other types that
// may be backed by memory if desired.
if (iree_io_file_handle_type(handle) !=
IREE_IO_FILE_HANDLE_TYPE_HOST_ALLOCATION) {
IREE_TRACE_ZONE_END(z0);
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"support for wrapping non-host-allocation file "
"handles with memory files is not yet implemented");
}

IREE_TRACE_ZONE_BEGIN(z0);

iree_byte_span_t contents = iree_io_file_handle_value(handle).host_allocation;

// Note that iree_device_size_t (for device offsets/sizes) may be smaller than
Expand Down Expand Up @@ -274,90 +275,55 @@ static void iree_hal_memory_file_try_import_buffer(
iree_status_ignore(status);
}

static const iree_hal_file_vtable_t iree_hal_memory_file_vtable = {
.destroy = iree_hal_memory_file_destroy,
};

//===----------------------------------------------------------------------===//
// EXPERIMENTAL: synchronous file read/write API
//===----------------------------------------------------------------------===//
// This is incomplete and may not appear like this on the iree_hal_file_t
// vtable; this does work for memory files though.

IREE_API_EXPORT iree_hal_memory_access_t
iree_hal_file_allowed_access(iree_hal_file_t* base_file) {
IREE_ASSERT_ARGUMENT(base_file);

// EXPERIMENTAL: today only memory files. This should be on the file vtable
// (if supported - not all implementations need to support it).
iree_hal_memory_file_t* file = (iree_hal_memory_file_t*)base_file;

static iree_hal_memory_access_t iree_hal_memory_file_allowed_access(
iree_hal_file_t* base_file) {
iree_hal_memory_file_t* file = iree_hal_memory_file_cast(base_file);
return file->access;
}

IREE_API_EXPORT uint64_t iree_hal_file_length(iree_hal_file_t* base_file) {
IREE_ASSERT_ARGUMENT(base_file);

// EXPERIMENTAL: today only memory files. This should be on the file vtable
// (if supported - not all implementations need to support it).
iree_hal_memory_file_t* file = (iree_hal_memory_file_t*)base_file;

static uint64_t iree_hal_memory_file_length(iree_hal_file_t* base_file) {
iree_hal_memory_file_t* file = iree_hal_memory_file_cast(base_file);
return file->storage->contents.data_length;
}

IREE_API_EXPORT iree_hal_buffer_t* iree_hal_file_storage_buffer(
static iree_hal_buffer_t* iree_hal_memory_file_storage_buffer(
iree_hal_file_t* base_file) {
IREE_ASSERT_ARGUMENT(base_file);

// EXPERIMENTAL: today only memory files. This should be on the file vtable
// (if supported - not all implementations need to support it).
iree_hal_memory_file_t* file = (iree_hal_memory_file_t*)base_file;

iree_hal_memory_file_t* file = iree_hal_memory_file_cast(base_file);
return file->imported_buffer;
}

IREE_API_EXPORT iree_status_t iree_hal_file_read(
iree_hal_file_t* base_file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length) {
IREE_ASSERT_ARGUMENT(base_file);
IREE_ASSERT_ARGUMENT(buffer);
IREE_TRACE_ZONE_BEGIN(z0);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, file_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)buffer_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)length);

// EXPERIMENTAL: today only memory files. This should be on the file vtable
// (if supported - not all implementations need to support it).
iree_hal_memory_file_t* file = (iree_hal_memory_file_t*)base_file;
static bool iree_hal_memory_file_supports_synchronous_io(
iree_hal_file_t* base_file) {
// Memory files always support synchronous IO.
return true;
}

// Copy from the file contents to the staging buffer.
static iree_status_t iree_hal_memory_file_read(iree_hal_file_t* base_file,
uint64_t file_offset,
iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset,
iree_device_size_t length) {
iree_hal_memory_file_t* file = iree_hal_memory_file_cast(base_file);
iree_byte_span_t file_contents = file->storage->contents;
iree_status_t status = iree_hal_buffer_map_write(
buffer, buffer_offset, file_contents.data + file_offset, length);

IREE_TRACE_ZONE_END(z0);
return status;
return iree_hal_buffer_map_write(buffer, buffer_offset,
file_contents.data + file_offset, length);
}

IREE_API_EXPORT iree_status_t iree_hal_file_write(
static iree_status_t iree_hal_memory_file_write(
iree_hal_file_t* base_file, uint64_t file_offset, iree_hal_buffer_t* buffer,
iree_device_size_t buffer_offset, iree_device_size_t length) {
IREE_ASSERT_ARGUMENT(base_file);
IREE_ASSERT_ARGUMENT(buffer);
IREE_TRACE_ZONE_BEGIN(z0);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, file_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)buffer_offset);
IREE_TRACE_ZONE_APPEND_VALUE_I64(z0, (int64_t)length);

// EXPERIMENTAL: today only memory files. This should be on the file vtable
// (if supported - not all implementations need to support it).
iree_hal_memory_file_t* file = (iree_hal_memory_file_t*)base_file;

// Copy from the staging buffer to the file contents.
iree_hal_memory_file_t* file = iree_hal_memory_file_cast(base_file);
iree_byte_span_t file_contents = file->storage->contents;
iree_status_t status = iree_hal_buffer_map_read(
buffer, buffer_offset, file_contents.data + file_offset, length);

IREE_TRACE_ZONE_END(z0);
return status;
return iree_hal_buffer_map_read(buffer, buffer_offset,
file_contents.data + file_offset, length);
}

static const iree_hal_file_vtable_t iree_hal_memory_file_vtable = {
.destroy = iree_hal_memory_file_destroy,
.allowed_access = iree_hal_memory_file_allowed_access,
.length = iree_hal_memory_file_length,
.storage_buffer = iree_hal_memory_file_storage_buffer,
.supports_synchronous_io = iree_hal_memory_file_supports_synchronous_io,
.read = iree_hal_memory_file_read,
.write = iree_hal_memory_file_write,
};
Loading

0 comments on commit 5b67943

Please sign in to comment.