Skip to content
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

experimental wrapper types for cudaEvent_t that provide a modern C++ interface. #2017

Merged
merged 18 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
24 changes: 24 additions & 0 deletions cudax/include/cuda/experimental/__detail/utility.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDA Experimental in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef __CUDAX_DETAIL_UTILITY_H
#define __CUDAX_DETAIL_UTILITY_H

namespace cuda::experimental
{
struct uninit_t
{
explicit uninit_t() = default;
};

inline constexpr uninit_t uninit{};
ericniebler marked this conversation as resolved.
Show resolved Hide resolved
} // namespace cuda::experimental
miscco marked this conversation as resolved.
Show resolved Hide resolved

#endif // __CUDAX_DETAIL_UTILITY_H
162 changes: 162 additions & 0 deletions cudax/include/cuda/experimental/__event/event.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDA Experimental in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _CUDAX_EVENT_DETAIL_H
#define _CUDAX_EVENT_DETAIL_H

#include <cuda_runtime_api.h>
// cuda_runtime_api needs to come first

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
# pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
# pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
# pragma system_header
#endif // no system header

#include <cuda/std/__cuda/api_wrapper.h>
#include <cuda/std/__exception/cuda_error.h>
#include <cuda/std/cstddef>
#include <cuda/std/utility>
#include <cuda/stream_ref>
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

// CUDAX headers here
#include <cuda/experimental/__detail/utility.cuh>
#include <cuda/experimental/__event/event_ref.cuh>
miscco marked this conversation as resolved.
Show resolved Hide resolved

#include <cassert>
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

namespace cuda::experimental
{
class timed_event;

/**
* @brief An owning wrapper for an untimed `cudaEvent_t`.
*/
ericniebler marked this conversation as resolved.
Show resolved Hide resolved
class event : public event_ref
{
friend class timed_event;

public:
/**
* @brief Flags to use when creating the event.
*/
enum class flags : unsigned int
{
none = cudaEventDefault,
blocking_sync = cudaEventBlockingSync,
interprocess = cudaEventInterprocess
};

/**
* @brief Construct a new `event` object with timing disabled.
*
* @throws cuda_error if the event creation fails.
*/
event()
: event(static_cast<unsigned int>(cudaEventDisableTiming))
{}

/**
* @brief Construct a new `event` object with the specified flags.
*
* @throws cuda_error if the event creation fails.
*/
explicit event(flags __flags)
: event(static_cast<unsigned int>(__flags) | static_cast<unsigned int>(cudaEventDisableTiming))
{}

/**
* @brief Construct a new `event` object into the moved-from state.
*
* @post `get()` returns `cudaEvent_t()`.
*/
explicit constexpr event(uninit_t) noexcept {}

/**
* @brief Move-construct a new `event` object
*
* @param __other
*
* @post `__other` is in a moved-from state.
*/
constexpr event(event&& __other) noexcept
ericniebler marked this conversation as resolved.
Show resolved Hide resolved
: event_ref(_CUDA_VSTD::exchange(__other.__event_, {}))
{}

/**
* @brief Destroy the `event` object
*
* @note If the event fails to be destroyed, the error is silently ignored.
*/
~event()
{
if (__event_ != nullptr)
{
[[maybe_unused]] auto __status = ::cudaEventDestroy(__event_);
}
}

/**
* @brief Construct an `event` object from a native `cudaEvent_t` handle.
*
* @param __evnt The native handle
*
* @return event The constructed `event` object
*
* @note The constructed `event` object takes ownership of the native handle.
*/
_CCCL_NODISCARD static event from_native_handle(::cudaEvent_t __evnt) noexcept
{
return event(__evnt);
}
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

/// Disallow construction from an `int`, e.g., `0`.
static event from_native_handle(int) = delete;

/// Disallow construction from `nullptr`.
static event from_native_handle(_CUDA_VSTD::nullptr_t) = delete;

/**
* @brief Retrieve the native `cudaEvent_t` handle and give up ownership.
*
* @return cudaEvent_t The native handle being held by the `event` object.
*
* @post The event object is in a moved-from state.
*/
_CCCL_NODISCARD constexpr ::cudaEvent_t release() noexcept
{
return _CUDA_VSTD::exchange(__event_, {});
}

_CCCL_NODISCARD_FRIEND constexpr flags operator|(flags __lhs, flags __rhs) noexcept
{
return static_cast<flags>(static_cast<unsigned int>(__lhs) | static_cast<unsigned int>(__rhs));
}

private:
// Use `event::from_native_handle(e)` to construct an owning `event`
// object from a `cudaEvent_t` handle.
explicit constexpr event(::cudaEvent_t __evnt) noexcept
: event_ref(__evnt)
{}

explicit event(unsigned int __flags)
{
_CCCL_TRY_CUDA_API(
::cudaEventCreateWithFlags, "Failed to create CUDA event", &__event_, static_cast<unsigned int>(__flags));
}
};
} // namespace cuda::experimental

#endif // _CUDAX_EVENT_DETAIL_H
149 changes: 149 additions & 0 deletions cudax/include/cuda/experimental/__event/event_ref.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDA Experimental in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _CUDAX_EVENT_REF_DETAIL_H
#define _CUDAX_EVENT_REF_DETAIL_H

#include <cuda_runtime_api.h>
// cuda_runtime_api needs to come first

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
# pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
# pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
# pragma system_header
#endif // no system header

#include <cuda/std/__cuda/api_wrapper.h>
#include <cuda/std/__exception/cuda_error.h>
#include <cuda/std/cstddef>
#include <cuda/std/utility>
#include <cuda/stream_ref>

#include <cassert>

namespace cuda::experimental
{
class event;
class timed_event;

/**
* @brief An non-owning wrapper for an untimed `cudaEvent_t`.
*/
class event_ref
{
private:
friend class event;
friend class timed_event;

::cudaEvent_t __event_{0};
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

public:
using value_type = ::cudaEvent_t;

/**
* @brief Construct a new `event_ref` that does refer to an event.
*/
event_ref() = default;
ericniebler marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief Construct a new `event_ref` object from a `cudaEvent_t`
*
* This constructor provides an implicit conversion from `cudaEvent_t`
*
* @post `get() == __evnt`
*
* @note: It is the callers responsibilty to ensure the `event_ref` does not
* outlive the event denoted by the `cudaEvent_t` handle.
*/
constexpr event_ref(::cudaEvent_t __evnt) noexcept
: __event_(__evnt)
{}

/// Disallow construction from an `int`, e.g., `0`.
event_ref(int) = delete;

/// Disallow construction from `nullptr`.
event_ref(_CUDA_VSTD::nullptr_t) = delete;

/**
* @brief Records an event on the specified stream
*
* @param __stream
*
* @throws cuda_error if the event record fails
*/
void record(stream_ref __stream)
{
assert(__event_ != nullptr);
assert(__stream.get() != nullptr);
_CCCL_TRY_CUDA_API(::cudaEventRecord, "Failed to record CUDA event", __event_, __stream.get());
}

/**
* @brief Waits for a CUDA event_ref to complete on the specified stream
*
* @param __stream The stream to wait on
*
* @throws cuda_error if the event_ref wait fails
*/
void wait(stream_ref __stream) const
{
assert(__event_ != nullptr);
assert(__stream.get() != nullptr);
_CCCL_TRY_CUDA_API(::cudaStreamWaitEvent, "Failed to wait for CUDA event", __stream.get(), __event_);
}

/**
* @brief Retrieve the native `cudaEvent_t` handle.
*
* @return cudaEvent_t The native handle being held by the event_ref object.
*/
_CCCL_NODISCARD constexpr ::cudaEvent_t get() const noexcept
{
return __event_;
}

/**
* @brief Compares two `event_ref`s for equality
*
* @note Allows comparison with `cudaEvent_t` due to implicit conversion to
* `event_ref`.
*
* @param lhs The first `event_ref` to compare
* @param rhs The second `event_ref` to compare
* @return true if `lhs` and `rhs` refer to the same `cudaEvent_t` object.
*/
_CCCL_NODISCARD_FRIEND constexpr bool operator==(event_ref __lhs, event_ref __rhs) noexcept
{
return __lhs.__event_ == __rhs.__event_;
}

/**
* @brief Compares two `event_ref`s for inequality
*
* @note Allows comparison with `cudaEvent_t` due to implicit conversion to
* `event_ref`.
*
* @param lhs The first `event_ref` to compare
* @param rhs The second `event_ref` to compare
* @return true if `lhs` and `rhs` refer to different `cudaEvent_t` objects.
*/
_CCCL_NODISCARD_FRIEND constexpr bool operator!=(event_ref __lhs, event_ref __rhs) noexcept
{
return __lhs.__event_ != __rhs.__event_;
}
miscco marked this conversation as resolved.
Show resolved Hide resolved
};
} // namespace cuda::experimental

#endif // _CUDAX_EVENT_REF_DETAIL_H
Loading
Loading