-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjust the way of determining FileHandle's compatibility mode for sync and async I/O to improve code readability #608
base: branch-25.04
Are you sure you want to change the base?
Conversation
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
/ok to test |
/ok to test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a good idea but I am wondering if we should go a step further and implement a ResolveCompatMode
class that encapsulate ALL compat-mode logic?
I image that |
Right, I am not sure, it just think it would simplify the code to have all the compat infer code in one place, or maybe not :) |
I see. |
/ok to test |
466301c
to
4e0c305
Compare
cpp/include/kvikio/compat_mode.hpp
Outdated
// Enable documentation of the enum. | ||
/** | ||
* @file | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should #pragma once
and the includes go before this documentation block? Does this documentation block need to have any content?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the order of this command and #pragma once
does not matter here. The block doesn't need any content. The presence of @file
alone enables the enum
doc generation.
But on a second thought, perhaps a better option is to simply document the namespace kvikio in the defaults.hpp
. This would automatically generate docs for all namespace scope entities. What do you think @bdice ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we try to keep #pragma once
at the top of the file. Your proposal to automatically generate docs sounds reasonable on the surface but I am not familiar enough with how Doxygen works to have strong opinions here.
cpp/include/kvikio/compat_mode.hpp
Outdated
enum class CompatMode : uint8_t { | ||
OFF, ///< Enforce cuFile I/O. GDS will be activated if the system requirements for cuFile are met | ||
///< and cuFile is properly configured. However, if the system is not suited for cuFile, I/O | ||
///< operations under the OFF option may error out, crash or hang. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors are acceptable. Is there a way to prevent crashes and hangs? It seems like we should be able to error in any situation where we would have otherwise fallen back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for noticing this. I meant to say "unspecified behavior" and wanted to give a couple of examples, but it looks like returning the non-success error code is my general observation. So I'll remove the "crash or hang" part from here and the doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
cpp/include/kvikio/file_handle.hpp
Outdated
#include <kvikio/parallel_operation.hpp> | ||
#include <kvikio/posix_io.hpp> | ||
#include <kvikio/shim/cufile.hpp> | ||
#include <kvikio/stream.hpp> | ||
#include <kvikio/utils.hpp> | ||
#include "kvikio/shim/cufile_h_wrapper.hpp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include "kvikio/shim/cufile_h_wrapper.hpp" | |
#include <kvikio/shim/cufile_h_wrapper.hpp> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Done.
cpp/include/kvikio/file_utils.hpp
Outdated
#include <optional> | ||
#include <string> | ||
|
||
#include "kvikio/shim/cufile_h_wrapper.hpp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include "kvikio/shim/cufile_h_wrapper.hpp" | |
#include <kvikio/shim/cufile_h_wrapper.hpp> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
cpp/src/file_handle.cpp
Outdated
|
||
#include <kvikio/defaults.hpp> | ||
#include <kvikio/file_handle.hpp> | ||
#include <kvikio/file_utils.hpp> | ||
#include "kvikio/compat_mode.hpp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include "kvikio/compat_mode.hpp" | |
#include <kvikio/compat_mode.hpp> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
cpp/include/kvikio/compat_mode.hpp
Outdated
/** | ||
* @brief Parse a string into a CompatMode enum. | ||
* | ||
* @param compat_mode_str Compatibility mode in string format(case-insensitive). Valid values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @param compat_mode_str Compatibility mode in string format(case-insensitive). Valid values | |
* @param compat_mode_str Compatibility mode in string format (case-insensitive). Valid values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Done.
CompatMode parse_compat_mode_str(std::string_view compat_mode_str); | ||
|
||
} // namespace detail | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs of CompatModeManager
would be good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! Will do!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
cpp/include/kvikio/compat_mode.hpp
Outdated
bool is_compat_mode_preferred(CompatMode compat_mode) noexcept; | ||
|
||
std::tuple<FileWrapper, FileWrapper, CUFileHandleWrapper, bool, bool> | ||
resolve_compat_mode_for_file(std::string const& file_path, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs of resolve_compat_mode_for_file
would be good
cpp/src/file_handle.cpp
Outdated
_is_compat_mode_preferred, | ||
_is_compat_mode_preferred_for_async) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about including them in the state of CompatModeManager
?
And replace compat_mode_requested
, is_compat_mode_preferred
, is_compat_mode_preferred_for_async
. with a const reference to a CompatModeManager
instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! Implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, my current implementation is to make the defaults
singleton hold a CompatModeManager
as its data member, and likewise make each FileHandle
hold a per-instance CompatModeManager
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kingcrimsontianyu If it is not too much work, I think it would help review and design discussion if you move FileWrapper
to its own PR?
cpp/include/kvikio/compat_mode.hpp
Outdated
CompatModeManager() = default; | ||
~CompatModeManager() noexcept = default; | ||
CompatModeManager(const CompatModeManager&) = delete; | ||
CompatModeManager& operator=(const CompatModeManager&) = delete; | ||
CompatModeManager(CompatModeManager&&) noexcept; | ||
CompatModeManager& operator=(CompatModeManager&&) noexcept; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CompatModeManager() = default; | |
~CompatModeManager() noexcept = default; | |
CompatModeManager(const CompatModeManager&) = delete; | |
CompatModeManager& operator=(const CompatModeManager&) = delete; | |
CompatModeManager(CompatModeManager&&) noexcept; | |
CompatModeManager& operator=(CompatModeManager&&) noexcept; |
Suggest to make all of this default. I think it is fine making CompatModeManager
copyable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we still keep the move constructor and move assignment operator explicitly defined? If made default
, the moved-from object will not have proper "reset" values. The std::exchange()
used in the current explicitly defined version avoids this problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to reset the values? I mean, if CompatModeManager
is accessed after move, I don't think resetting its values helps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I was thinking of is something like this:
// std::exchange currently used in FileHandle's move constructor "resets" old_file_handle's fd to -1.
auto new_file_handle = std::move(old_file_handle);
// So this is valid, as fd has a specified value.
query(old_file_handle.fd());
// But small whoopsy. CompatModeManager is default moved.
// After moved from, all the data members of `old_file_handle._compat_mode_manager` are left unspecified.
query(old_file_handle.compat_mode_requested());
query(old_file_handle.is_compat_mode_preferred());
query(old_file_handle.is_compat_mode_preferred_for_async());
Maybe I worried too much about this. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is a bit too defensive :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! I'll simply default everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
cpp/src/compat_mode.cpp
Outdated
} | ||
|
||
std::tuple<FileWrapper, FileWrapper, CUFileHandleWrapper> | ||
CompatModeManager::resolve_compat_mode_for_file(std::string const& file_path, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be moved to the ctor? I think it is a bit surprising that it changes the state of CompatModeManager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial intention is to default initialize CompatModeManager
, and derive the data member at a later time, i.e:
- For the compat mode manager in
defaults
class that does not involve files, indefaults
's constructor usecompat_mode_reset()
to save the requested compat mode and determine the final compat mode. - For the compat mode manager in
FileHandle
class, useresolve_compat_mode_for_file
to save the requested compat mode and determine the final compat mode for sync and async I/O (this function also returns file objects that the constructor cannot do).
Would it be better if we:
- Still use
CompatModeManager
for thedefaults
class, but remove any async-related data member and method. - Have a subclass
CompatModeForFileManager
to include the async-related data member and method, put the "resolve_compat_mode_for_file" logic to the constructor as you suggested, and have a getter method to retrieve the generated file and handle objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about keeping defaults.cpp
as it was prior to this PR and then introduce CompatModeManager
for file handlers?
I don't think the current defaults.cpp
code it too complex. The complex starts when files are involved :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverting defaults.cpp
will do.
The flip side would be a slight impediment to mocking. I had intended to thoroughly mock the following code, including the is_cufile_available()
function:
Line 146 in b5758ec
CompatMode defaults::infer_compat_mode_if_auto(CompatMode compat_mode) noexcept |
Line 157 in b5758ec
bool defaults::is_compat_mode_preferred(CompatMode compat_mode) noexcept |
A compromise I could imagine would be to simply have these constructor overloads:
// For defaults
// File-related members are default initialized. No actual files or handles are involved.
CompatModeManager();
// For FileHandle
CompatModeManager(std::string const& file_path, std::string const& flags, mode_t mode, CompatMode compat_mode_requested);
That is to tolerate the dual use of CompatModeManager
for defaults
and FileHandle
, with the slight benefit of thorough mocking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could also work, but I must admit, I prefer avoiding the dual use of CompatModeManager
altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! I'll restrict CompatModeManager
to file only and revert defaults
.
To facilitate mocking in another PR I'll also keep infer_compat_mode_if_auto
and is_compat_mode_preferred
in CompatModeManager
(mild code duplication).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
Reducing the scope of this PR as suggested. File RAII addressed in another PR: #614 |
If I directly rebase this branch on 25.04, there would too many repeated rebase conflicts to resolve (as a result of some commits on this branch making back-and-forth changes). But I do have a branch where I simply squash the commits of this branch and then rebase to 25.04. Should I force-push that branch to here? Or should I close this PR and open a new one for the squashed-commit branch? I asked with the hope to not lose the review comments above. Thanks for any suggestion! @bdice |
Squash and force-push is fine. |
fe29675
to
1950a10
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, I only have an one suggestion
cpp/include/kvikio/file_handle.hpp
Outdated
|
||
/** | ||
* @brief Returns the initially requested compatibility mode. | ||
* | ||
* @return The compatibility mode initially requested. | ||
*/ | ||
CompatMode compat_mode_requested() const noexcept; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe return a const reference to _compat_mode_manager
instead of compat_mode_requested
, is_compat_mode_preferred
, and is_compat_mode_preferred_for_async
. Most users will never have to use them, so I think an extra indirection is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. Done!
Updated the PR to reflect this slightly breaking change.
2f9b2f2
to
790c40e
Compare
This PR improves the readability of compatibility mode handling.
The current way of determining FileHandle's compatibility mode is somewhat complicated and unintuitive. The data member
_compat_mode
accompanied by some utility functions more or less combines 3 different things into one:ON
/OFF
/AUTO
)The disadvantages include:
FileHandle::is_compat_mode_preferred()
always derives the preferred compat mode on the fly as opposed to getting an already determined value.FileHandle::is_compat_mode_preferred_for_async(CompatMode)
is potentially throwing, which is asymmetric tois_compat_mode_preferred()
. Also when the compat mode isOFF
, it has to invokeis_stream_api_available()
andconfig_path()
on each pass instead of getting an already determined value.These add to cognitive burden when rereading the source to introduce new features to FileHandle. This PR attempts to improve the logic by making it concise and crystal clear.
This PR also fixes a line number bug in error handling.
This PR is breaking in that the rarely used public functions to query the compat mode data in the
FileHandle
are removed. These data are instead queryable via the newCompatModeManager
class.