Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
916028a
Support host-side file-back mapping
kingcrimsontianyu May 16, 2025
f40847d
Merge branch 'branch-25.06' into host-mmap-read
kingcrimsontianyu May 16, 2025
963a6d9
Merge branch 'branch-25.06' into host-mmap-read
kingcrimsontianyu May 20, 2025
5c1c461
Merge remote-tracking branch 'origin/branch-25.08' into host-mmap-read
kingcrimsontianyu May 22, 2025
9c272db
Update
kingcrimsontianyu May 27, 2025
4c8c1aa
Update
kingcrimsontianyu May 27, 2025
1ee400d
Merge branch 'branch-25.08' into host-mmap-read
kingcrimsontianyu May 27, 2025
8fb0676
Update
kingcrimsontianyu May 28, 2025
ae2da3f
Update
kingcrimsontianyu May 28, 2025
fea5abd
Update
kingcrimsontianyu May 29, 2025
6d04caa
Update
kingcrimsontianyu May 29, 2025
aacdc8a
Merge branch 'branch-25.08' into host-mmap-read
kingcrimsontianyu May 29, 2025
72138df
Fix typo
kingcrimsontianyu May 29, 2025
2794474
Fix bugs
kingcrimsontianyu May 30, 2025
74cbe33
Bug fixes
kingcrimsontianyu May 31, 2025
91643b2
Prepare to add comments
kingcrimsontianyu May 31, 2025
733d142
Merge branch 'branch-25.08' into host-mmap-read
kingcrimsontianyu May 31, 2025
576d929
Merge remote-tracking branch 'origin/host-mmap-read' into host-mmap-read
kingcrimsontianyu May 31, 2025
99c8213
Update
kingcrimsontianyu May 31, 2025
606b233
Add the utility function to clear page cache
kingcrimsontianyu Jun 1, 2025
b8ca03d
Update
kingcrimsontianyu Jun 1, 2025
30721e8
Merge branch 'clear-page-cache' into example-host-mmap-read
kingcrimsontianyu Jun 2, 2025
e4fd08f
Update
kingcrimsontianyu Jun 2, 2025
413d1b9
Update
kingcrimsontianyu Jun 2, 2025
9ba73fb
Merge branch 'branch-25.08' into host-mmap-read
kingcrimsontianyu Jun 2, 2025
54c51bc
Merge branch 'branch-25.08' into clear-page-cache
kingcrimsontianyu Jun 2, 2025
2dc0f1c
Merge branch 'branch-25.08' into example-host-mmap-read
kingcrimsontianyu Jun 2, 2025
f1e5a46
Simplify impl
kingcrimsontianyu Jun 3, 2025
314de3f
Update
kingcrimsontianyu Jun 3, 2025
ad2ed90
Merge branch 'host-mmap-read' into example-host-mmap-read
kingcrimsontianyu Jun 3, 2025
83be7db
Update
kingcrimsontianyu Jun 3, 2025
f9c4d17
Update
kingcrimsontianyu Jun 4, 2025
1416bb1
Update
kingcrimsontianyu Jun 4, 2025
25e82fa
Update
kingcrimsontianyu Jun 4, 2025
0082e52
Update
kingcrimsontianyu Jun 4, 2025
7051979
Update
kingcrimsontianyu Jun 4, 2025
6300789
Update
kingcrimsontianyu Jun 4, 2025
0c5c2af
Add device read
kingcrimsontianyu Jun 4, 2025
9762503
Update
kingcrimsontianyu Jun 4, 2025
4b3017f
Update
kingcrimsontianyu Jun 4, 2025
d84a329
Update
kingcrimsontianyu Jun 4, 2025
e22ea53
Sort for __init__
kingcrimsontianyu Jun 5, 2025
763d7bd
Remove unused header
kingcrimsontianyu Jun 5, 2025
6b662d4
Merge branch 'branch-25.08' into host-mmap-read
kingcrimsontianyu Jun 5, 2025
44bd396
Update
kingcrimsontianyu Jun 6, 2025
f1fc66b
Merge branch 'branch-25.08' into clear-page-cache
madsbk Jun 6, 2025
cb6e730
Add detailed comment
kingcrimsontianyu Jun 10, 2025
bb68d17
Merge remote-tracking branch 'origin/clear-page-cache' into clear-pag…
kingcrimsontianyu Jun 10, 2025
0bb9b69
Update python comment
kingcrimsontianyu Jun 10, 2025
07c2f88
Update
kingcrimsontianyu Jun 11, 2025
a297552
Update
kingcrimsontianyu Jun 11, 2025
de979d6
Update
kingcrimsontianyu Jun 12, 2025
cc48c4f
Merge branch 'host-mmap-read' into example-host-mmap-read
kingcrimsontianyu Jun 12, 2025
2db78fb
Merge branch 'clear-page-cache' into example-host-mmap-read
kingcrimsontianyu Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ set(SOURCES
"src/error.cpp"
"src/file_handle.cpp"
"src/file_utils.cpp"
"src/mmap.cpp"
"src/nvtx.cpp"
"src/posix_io.cpp"
"src/shim/cuda.cpp"
Expand Down
20 changes: 20 additions & 0 deletions cpp/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,23 @@ install(
DESTINATION ${TEST_INSTALL_PATH}
EXCLUDE_FROM_ALL
)

# Example: mmap_io_host

add_executable(MMAP_IO_HOST_EXAMPLE mmap_io_host.cpp)
set_target_properties(MMAP_IO_HOST_EXAMPLE PROPERTIES INSTALL_RPATH "\$ORIGIN/../../../lib")
target_link_libraries(MMAP_IO_HOST_EXAMPLE PRIVATE kvikio::kvikio)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(KVIKIO_CXX_FLAGS "-Wall;-Werror;-Wno-unknown-pragmas")
target_compile_options(
MMAP_IO_HOST_EXAMPLE PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${KVIKIO_CXX_FLAGS}>"
)
endif()

install(
TARGETS MMAP_IO_HOST_EXAMPLE
COMPONENT testing
DESTINATION ${TEST_INSTALL_PATH}
EXCLUDE_FROM_ALL
)
163 changes: 163 additions & 0 deletions cpp/examples/mmap_io_host.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2025, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <chrono>
#include <iostream>
#include <ratio>
#include <string>
#include <vector>

#include <kvikio/file_handle.hpp>
#include <kvikio/file_utils.hpp>
#include <kvikio/mmap.hpp>
#include "kvikio/defaults.hpp"

std::string parse_cmd(int argc, char* argv[]) { return (argc > 1) ? argv[1] : "/tmp"; }

class Timer {
public:
void start() { _start = std::chrono::high_resolution_clock::now(); }
double elapsed_time()
{
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::micro> time_elapsed = end - _start;
auto result = time_elapsed.count();
return result;
}

private:
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
};

class IoHostManager {
public:
IoHostManager(std::string const& test_dir = "/tmp", bool clear_page_cache = false)
: _test_filepath(test_dir + "/test-file"),
_clear_page_cache(clear_page_cache),
_data_size(1024ull * 1024ull * 1024ull),
_num_repetition(10)
{
kvikio::defaults::set_num_threads(8);
std::vector<std::byte> v(_data_size, {});
kvikio::FileHandle file_handle(_test_filepath, "w");
auto fut = file_handle.pwrite(v.data(), v.size());
fut.get();
}

void use_standard_io_parallel()
{
std::cout << "Standard I/O\n";
std::vector<std::byte> v(_data_size, {});
double ave_init_time{0.0};
double ave_io_time{0.0};

for (std::size_t i = 0; i < _num_repetition; ++i) {
if (_clear_page_cache) { kvikio::clear_page_cache(); }
print_page_cache_info();

Timer timer;
timer.start();
kvikio::FileHandle file_handle(_test_filepath, "r");
ave_init_time += timer.elapsed_time();

timer.start();
auto fut = file_handle.pread(v.data(), _data_size);
fut.get();
ave_io_time += timer.elapsed_time();
}

std::cout << " Average initialization time: " << ave_init_time / _num_repetition << "\n";
std::cout << " Average I/O time: " << ave_io_time / _num_repetition << "\n";
}

void use_mmap_io_seq()
{
std::cout << "Mmap I/O (sequential)\n";
std::vector<std::byte> v(_data_size, {});
double ave_init_time{0.0};
double ave_io_time{0.0};

for (std::size_t i = 0; i < _num_repetition; ++i) {
if (_clear_page_cache) { kvikio::clear_page_cache(); }
print_page_cache_info();

Timer timer;
timer.start();
kvikio::MmapHandle mmap_handle(_test_filepath, "r");
ave_init_time += timer.elapsed_time();

timer.start();
mmap_handle.read(v.data(), _data_size);
ave_io_time += timer.elapsed_time();
}

std::cout << " Average initialization time: " << ave_init_time / _num_repetition << "\n";
std::cout << " Average I/O time: " << ave_io_time / _num_repetition << "\n";
}

void use_mmap_io_parallel()
{
std::cout << "Mmap I/O (parallel)\n";
std::vector<std::byte> v(_data_size, {});
double ave_init_time{0.0};
double ave_io_time{0.0};

for (std::size_t i = 0; i < _num_repetition; ++i) {
if (_clear_page_cache) { kvikio::clear_page_cache(); }
print_page_cache_info();

Timer timer;
timer.start();
kvikio::MmapHandle mmap_handle(_test_filepath, "r");
ave_init_time += timer.elapsed_time();

timer.start();
auto fut = mmap_handle.pread(v.data(), _data_size);
fut.get();
ave_io_time += timer.elapsed_time();
}

std::cout << " Average initialization time: " << ave_init_time / _num_repetition << "\n";
std::cout << " Average I/O time: " << ave_io_time / _num_repetition << "\n";
}

private:
void print_page_cache_info()
{
auto const [num_pages_in_page_cache, num_pages] = kvikio::get_page_cache_info(_test_filepath);
std::cout << " Page cache residency ratio: "
<< static_cast<double>(num_pages_in_page_cache) / static_cast<double>(num_pages)
<< "\n";
}
std::string _test_filepath;
bool _clear_page_cache;
std::size_t _data_size;
std::size_t _num_repetition;
};

int main()
{
// auto test_dir = parse_cmd(argc, argv);
IoHostManager io_host_manager{"/mnt/nvme", false};

io_host_manager.use_mmap_io_seq();

io_host_manager.use_mmap_io_parallel();

io_host_manager.use_standard_io_parallel();

return 0;
}
5 changes: 5 additions & 0 deletions cpp/include/kvikio/defaults.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class defaults {
std::size_t _http_max_attempts;
long _http_timeout;
std::vector<int> _http_status_codes;
std::size_t _mmap_task_size;

static unsigned int get_num_threads_from_env();

Expand Down Expand Up @@ -367,6 +368,10 @@ class defaults {
* @param status_codes The HTTP status codes to retry.
*/
static void set_http_status_codes(std::vector<int> status_codes);

[[nodiscard]] static std::size_t mmap_task_size();

static void set_mmap_task_size(std::size_t nbytes);
};

} // namespace kvikio
23 changes: 23 additions & 0 deletions cpp/include/kvikio/file_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,27 @@ std::pair<std::size_t, std::size_t> get_page_cache_info(std::string const& file_
* @sa `get_page_cache_info(std::string const&)` overload.
*/
std::pair<std::size_t, std::size_t> get_page_cache_info(int fd);

/**
* @brief Clear the page cache
*
* @param reclaim_dentries_and_inodes Whether to free reclaimable slab objects which include
* dentries and inodes.
* - If `true`, equivalent to executing `/sbin/sysctl vm.drop_caches=3`;
* - If `false`, equivalent to executing `/sbin/sysctl vm.drop_caches=1`.
* @param clear_dirty_pages Whether to trigger the writeback process to clear the dirty pages. If
* `true`, `sync` will be called prior to cache clearing.
* @return Whether the page cache has been successfully cleared
*
* @note This function creates a child process and executes the cache clearing shell command in the
* following order
* - Execute the command without `sudo` prefix. This is for the superuser and also for specially
* configured systems where unprivileged users cannot execute `/usr/bin/sudo` but can execute
* `/sbin/sysctl`. If this step succeeds, the function returns `true` immediately.
* - Execute the command with `sudo` prefix. This is for the general case where selective
* unprivileged users have permission to run `/sbin/sysctl` with `sudo` prefix.
*
* @throws kvikio::GenericSystemError if somehow the child process could not be created.
*/
bool clear_page_cache(bool reclaim_dentries_and_inodes = true, bool clear_dirty_pages = true);
} // namespace kvikio
148 changes: 148 additions & 0 deletions cpp/include/kvikio/mmap.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2025, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <cstddef>
#include <future>

#include <kvikio/defaults.hpp>
#include <kvikio/file_handle.hpp>
#include <optional>

namespace kvikio {

/**
* @brief
*
*/
class MmapHandle {
private:
void* _buf{};
std::size_t _initial_size{};
std::size_t _initial_file_offset{};
std::size_t _file_size{};
std::size_t _map_offset{};
std::size_t _map_size{};
void* _map_addr{};
bool _initialized{};
int _map_protection_flags{};
int _map_core_flags{};
FileWrapper _file_wrapper{};

/**
* @brief
*
* @param size
* @param file_offset
* @return
*/
std::tuple<void*, void*, void*, std::size_t> prepare_read(std::size_t size,
std::size_t file_offset);

public:
/**
* @brief Construct a new Mmap Handle object
*
*/
MmapHandle() noexcept = default;

/**
* @brief Construct a new Mmap Handle object
*
* @param file_path
* @param flags
* @param initial_size
* @param initial_file_offset
* @param mode
*/
MmapHandle(std::string const& file_path,
std::string const& flags = "r",
std::optional<std::size_t> initial_size = std::nullopt,
std::size_t initial_file_offset = 0,
mode_t mode = FileHandle::m644);

MmapHandle(MmapHandle const&) = delete;
MmapHandle& operator=(MmapHandle const&) = delete;
MmapHandle(MmapHandle&& o) noexcept;
MmapHandle& operator=(MmapHandle&& o) noexcept;
~MmapHandle() noexcept;

/**
* @brief
*
* @return std::size_t
*/
std::size_t initial_size() const noexcept;

/**
* @brief
*
* @return std::size_t
*/
std::size_t initial_file_offset() const noexcept;

/**
* @brief Get the file size
*
* @return The number of bytes
*/
[[nodiscard]] std::size_t file_size() const;

/**
* @brief Get the file size
*
* @return The number of bytes
*/
[[nodiscard]] std::size_t nbytes() const;

/**
* @brief
*
* @return Boolean answer
*/
[[nodiscard]] bool closed() const noexcept;

/**
* @brief
*
*/
void close() noexcept;

/**
* @brief
*
* @param size
* @param file_offset
* @return
*/
std::size_t read(void* buf,
std::optional<std::size_t> size = std::nullopt,
std::size_t file_offset = 0);

/**
* @brief
*
* @param size
* @param file_offset
* @param mmap_task_size
* @return
*/
std::future<std::size_t> pread(void* buf,
std::optional<std::size_t> size = std::nullopt,
std::size_t file_offset = 0,
std::size_t mmap_task_size = defaults::mmap_task_size());
};
} // namespace kvikio
Loading