observation.hpp
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #pragma once
7 
8 #include <chrono>
9 #include <cstddef>
10 #include <cstdint>
11 #include <string_view>
12 #include <type_traits>
13 
14 #include <kvikio/shim/utils.hpp>
15 
16 namespace KVIKIO_EXPORT kvikio {
17 
21 using Clock = std::chrono::steady_clock;
22 
26 using TimePoint = Clock::time_point;
27 
31 using Duration = std::chrono::nanoseconds;
32 
39 struct ClockAnchor {
41  TimePoint steady{};
43  std::chrono::system_clock::time_point wall{};
44 
54  [[nodiscard]] static ClockAnchor now() noexcept;
55 
66  [[nodiscard]] std::chrono::system_clock::time_point to_wall_clock(TimePoint time) const noexcept
67  {
68  // Chrono is not noexcept because a duration's representation may have throwing arithmetic, and
69  // duration_cast is specified in terms of the same operations. Ours are integers, so nothing
70  // here can throw.
71  static_assert(std::is_integral_v<TimePoint::rep>);
72  static_assert(std::is_integral_v<std::chrono::system_clock::rep>);
73  return wall + std::chrono::duration_cast<std::chrono::system_clock::duration>(time - steady);
74  }
75 };
76 
80 enum class IoBackend : std::uint8_t {
81  POSIX = 0,
82  GDS,
83  MMAP,
84  REMOTE_HTTP,
86 };
87 
91 constexpr std::size_t num_io_backends = static_cast<std::size_t>(IoBackend::REMOTE_HDFS) + 1;
92 
96 enum class TransferDirection : std::uint8_t {
97  READ = 0,
98  WRITE
99 };
100 
104 enum class MemoryKind : std::uint8_t {
105  HOST = 0,
106  DEVICE
107 };
108 
112 enum class ObservationKind : std::uint8_t {
113  LOGICAL = 0
114 };
115 
122 [[nodiscard]] std::string_view to_string(IoBackend backend) noexcept;
123 
130 [[nodiscard]] std::string_view to_string(TransferDirection direction) noexcept;
131 
138 [[nodiscard]] std::string_view to_string(MemoryKind memory_kind) noexcept;
139 
146 [[nodiscard]] std::string_view to_string(ObservationKind kind) noexcept;
147 
153 struct Observation {
155  TimePoint start{};
157  TimePoint end{};
159  std::size_t offset{};
161  std::size_t size{};
164  std::size_t bytes_transferred{};
166  std::uint64_t id{};
167 
169  char const* http_method{nullptr};
170 
173  std::string_view source{};
174 
176  ObservationKind kind{ObservationKind::LOGICAL};
178  IoBackend backend{IoBackend::POSIX};
180  TransferDirection direction{TransferDirection::READ};
182  MemoryKind memory_kind{MemoryKind::HOST};
184  bool ok{true};
185 
191  [[nodiscard]] Duration duration() const noexcept
192  {
193  return end > start ? end - start : Duration::zero();
194  }
195 
204  [[nodiscard]] double bytes_per_sec() const noexcept
205  {
206  auto const seconds = std::chrono::duration<double>{duration()}.count();
207  return seconds > 0.0 ? static_cast<double>(bytes_transferred) / seconds : 0.0;
208  }
209 };
210 
256 class Monitor {
257  public:
258  Monitor() = default;
259  virtual ~Monitor() = default;
260  Monitor(Monitor const&) = delete;
261  Monitor& operator=(Monitor const&) = delete;
262 
275  virtual void on_start(Observation const& observation) noexcept = 0;
276 
289  virtual void on_finish(Observation const& observation) noexcept = 0;
290 };
291 
302 [[nodiscard]] std::uint64_t register_monitor(Monitor* monitor,
303  ObservationKind kind = ObservationKind::LOGICAL);
304 
313 void unregister_monitor(std::uint64_t id);
314 
315 } // namespace KVIKIO_EXPORT kvikio
Watches operations, from the moment they start until they finish.
virtual void on_finish(Observation const &observation) noexcept=0
An operation has completed.
virtual void on_start(Observation const &observation) noexcept=0
An operation has started.
KvikIO namespace.
Definition: batch.hpp:16
ObservationKind
What layer an observation describes.
@ LOGICAL
One user-facing call, such as one FileHandle::pread().
std::chrono::nanoseconds Duration
A length of time, in nanoseconds.
Definition: observation.hpp:31
std::chrono::steady_clock Clock
The clock KvikIO timestamps observations with.
Definition: observation.hpp:21
IoBackend
The I/O backend that carried out an operation.
Definition: observation.hpp:80
@ POSIX
POSIX pread/pwrite, including the compatibility-mode path.
@ MMAP
Memory-mapped file access.
@ REMOTE_HTTP
Remote I/O over HTTP(S), including S3.
@ GDS
cuFile / GPUDirect Storage.
@ REMOTE_HDFS
Remote I/O over WebHDFS.
std::string_view to_string(IoBackend backend) noexcept
Human-readable name of an I/O backend.
constexpr std::size_t num_io_backends
Number of IoBackend values.
Definition: observation.hpp:91
TransferDirection
The direction of an I/O operation.
Definition: observation.hpp:96
MemoryKind
The kind of memory the caller's buffer lives in.
@ HOST
Host (CPU) memory.
@ DEVICE
Device (GPU) memory.
void unregister_monitor(std::uint64_t id)
Unregister a monitor.
Clock::time_point TimePoint
A point in time on Clock.
Definition: observation.hpp:26
std::uint64_t register_monitor(Monitor *monitor, ObservationKind kind=ObservationKind::LOGICAL)
Register a monitor, which begins receiving both notifications.
A reading of Clock and of the wall clock, taken together.
Definition: observation.hpp:39
static ClockAnchor now() noexcept
Read both clocks, one immediately after the other.
One I/O operation, as observed by KvikIO.
double bytes_per_sec() const noexcept
Throughput of this single operation.
Duration duration() const noexcept
How long the operation took.