Coverage for cuda/core/_event.pyx: 89.34%
122 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 01:38 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 01:38 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7cimport cpython
8from libc.string cimport memcpy
9from cuda.bindings cimport cydriver
10from cuda.core._context cimport Context
11from cuda.core._resource_handles cimport (
12 ContextHandle,
13 EventHandle,
14 create_event_handle,
15 create_event_handle_ipc,
16 get_event_timing_enabled,
17 get_event_is_blocking_sync,
18 get_event_ipc_enabled,
19 get_event_device_id,
20 get_event_context,
21 as_intptr,
22 as_cu,
23 as_py,
24)
26from cuda.core._utils.cuda_utils cimport (
27 check_or_create_options,
28 HANDLE_RETURN
29)
31import cython
32from dataclasses import dataclass
33import multiprocessing
34from typing import TYPE_CHECKING
36from cuda.core._utils.cuda_utils import (
37 CUDAError,
38 check_multiprocessing_start_method,
39)
41if TYPE_CHECKING:
42 import cuda.bindings.driver # no-cython-lint
43 from cuda.core._device import Device
46@dataclass
47cdef class EventOptions:
48 """Customizable :obj:`~_event.Event` options.
50 Attributes
51 ----------
52 timing_enabled : bool, optional
53 Event will record timing data. (Default to False)
54 blocking_sync : bool, optional
55 If True, the event uses blocking synchronization: a CPU
56 thread that calls :meth:`Event.sync` blocks (yields) until
57 the event has completed. Otherwise (the default), the CPU
58 thread busy-waits until the event has completed.
59 (Default to False)
60 ipc_enabled : bool, optional
61 Event will be suitable for interprocess use.
62 Note that timing_enabled must be False. (Default to False)
64 """
66 timing_enabled: bool | None = False
67 blocking_sync: bool | None = False
68 ipc_enabled: bool | None = False
71cdef class Event:
72 """Represent a record at a specific point of execution within a CUDA stream.
74 Applications can asynchronously record events at any point in
75 the program. An event keeps a record of all previous work within
76 the last recorded stream.
78 Events can be used to monitor device's progress, query completion
79 of work up to event's record, help establish dependencies
80 between GPU work submissions, and record the elapsed time (in milliseconds)
81 on GPU:
83 .. code-block:: python
85 # To create events and record the timing:
86 s = Device().create_stream()
87 e1 = Device().create_event({"timing_enabled": True})
88 e2 = Device().create_event({"timing_enabled": True})
89 s.record(e1)
90 # ... run some GPU works ...
91 s.record(e2)
92 e2.sync()
93 print(f"time = {e2 - e1} milliseconds")
95 Directly creating an :obj:`~_event.Event` is not supported due to ambiguity,
96 and they should instead be created through a :obj:`~_stream.Stream` object.
98 """
100 def __init__(self, *args, **kwargs) -> None:
101 raise RuntimeError("Event objects cannot be instantiated directly. Please use Stream APIs (record).") 2bd
103 @staticmethod
104 cdef Event _init(type cls, int device_id, ContextHandle h_context, options, bint is_free):
105 cdef Event self = cls.__new__(cls) 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
106 cdef EventOptions opts = check_or_create_options(EventOptions, options, "Event options") 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
107 cdef unsigned int flags = 0x0 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
108 cdef bint timing_enabled = True 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
109 cdef bint is_blocking_sync = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
110 cdef bint ipc_enabled = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
111 self._ipc_descriptor = None 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
112 if not opts.timing_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
113 flags |= cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbs UbVbt q Wb| i j e a f b g c h d n } r B XbC ~ J abD K E w bbcbdbN L F x y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
114 timing_enabled = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbs UbVbt q Wb| i j e a f b g c h d n } r B XbC ~ J abD K E w bbcbdbN L F x y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
115 if opts.blocking_sync: 2A $ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
116 flags |= cydriver.CUevent_flags.CU_EVENT_BLOCKING_SYNC 1poabcdL
117 is_blocking_sync = True 1poabcdL
118 if opts.ipc_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
119 if is_free: 2i j e a f b g c h d n =c?ck l m
120 raise TypeError( 1n
121 "IPC-enabled events must be bound; use Stream.record for creation."
122 )
123 flags |= cydriver.CUevent_flags.CU_EVENT_INTERPROCESS 2i j e a f b g c h d n =c?ck l m
124 ipc_enabled = True 2i j e a f b g c h d n =c?ck l m
125 if timing_enabled: 2i j e a f b g c h d n =c?ck l m
126 raise TypeError("IPC-enabled events cannot use timing.") 2=c?c
127 cdef EventHandle h_event = create_event_handle( 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
128 h_context, flags, timing_enabled, is_blocking_sync, ipc_enabled, device_id)
129 if not h_event: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
130 raise RuntimeError("Failed to create CUDA event")
131 self._h_event = h_event 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
132 if ipc_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
133 _ = self.ipc_descriptor # eagerly populate the descriptor cache 1Aijeafbgchdnklm
134 return self 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v B XbC ~ J abD K E w bbcbdbN L F x G y k z YbZbeb0b1b2b3b4b5b6b7b8b9b!b#bfb$b%b'b(bgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBb)b*b+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcO wcxcyczcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c# 3c4cl m Eb5c6cM 7c8cFb9c!cGb#c$cH %c'cI (c)c*c+c,c-c.c/c:c;c
136 @staticmethod
137 cdef Event _from_handle(EventHandle h_event):
138 """Create an Event wrapping an existing EventHandle.
140 Metadata (timing, blocking_sync, ipc, device_id) is read from
141 the EventBox via pointer arithmetic — no fields are cached on
142 Event.
143 """
144 cdef Event self = Event.__new__(Event) 2@c[c]c^cp s o t q
145 self._h_event = h_event 2@c[c]c^cp s o t q
146 self._ipc_descriptor = None 2@c[c]c^cp s o t q
147 return self 2@c[c]c^cp s o t q
149 cpdef close(self):
150 """Destroy the event.
152 Releases the event handle. The underlying CUDA event is destroyed
153 when the last reference is released.
154 """
155 self._h_event.reset() 1$%'()*+,-./:;=?@[]^_`{|k
157 def __isub__(self, other: object):
158 return NotImplemented 2bb
160 def __rsub__(self, other: object):
161 return NotImplemented 2cb
163 def __sub__(self, other: Event) -> float:
164 # return self - other (in milliseconds)
165 cdef float timing
166 with nogil: 1ruvG
167 err = cydriver.cuEventElapsedTime(&timing, as_cu((<Event>other)._h_event), as_cu(self._h_event)) 1ruvG
168 if err == 0: 1ruvG
169 return timing 1uG
170 else:
171 if err == cydriver.CUresult.CUDA_ERROR_INVALID_HANDLE: 1ruv
172 if not self.is_timing_enabled or not other.is_timing_enabled: 1rv
173 explanation = (
174 "Both Events must be created with timing enabled in order to subtract them; " 1r
175 "use EventOptions(timing_enabled=True) when creating both events."
176 )
177 else:
178 explanation = (
179 "Both Events must be recorded before they can be subtracted; " 1v
180 "use Stream.record() to record both events to a stream."
181 )
182 elif err == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1ruv
183 explanation = (
184 "One or both events have not completed; " 1u
185 "use Event.sync(), Stream.sync(), or Device.sync() to wait for the events to complete "
186 "before subtracting them."
187 )
188 else:
189 raise CUDAError(err)
190 raise RuntimeError(explanation) 1ruv
192 def __hash__(self) -> int:
193 return hash(as_intptr(self._h_event)) 2~ abD dbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbFbGbH I
195 def __eq__(self, other: object) -> bool:
196 # Note: using isinstance because `Event` can be subclassed.
197 if not isinstance(other, Event): 2@c[c]c^cJ D K N z O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! # M H I
198 return NotImplemented 1NzOPQRSTUVWXYZ0123456789!#
199 cdef Event _other = <Event>other 2@c[c]c^cJ D K z M H I
200 return as_intptr(self._h_event) == as_intptr(_other._h_event) 2@c[c]c^cJ D K z M H I
202 def __repr__(self) -> str:
203 return f"<Event handle={as_intptr(self._h_event):#x}>" 2i j e a f b g c h d Eb
205 @property
206 def ipc_descriptor(self) -> IPCEventDescriptor:
207 """Descriptor for sharing this event with other processes."""
208 if self._ipc_descriptor is not None: 1ijeafbgchdnEklm
209 return self._ipc_descriptor 1ijeafbgchdklm
210 if not self.is_ipc_enabled: 1ijeafbgchdnEklm
211 raise RuntimeError("Event is not IPC-enabled") 1E
212 cdef cydriver.CUipcEventHandle data
213 with nogil: 1ijeafbgchdnklm
214 HANDLE_RETURN(cydriver.cuIpcGetEventHandle(&data, as_cu(self._h_event))) 1ijeafbgchdnklm
215 cdef bytes data_b = cpython.PyBytes_FromStringAndSize(<char*>(data.reserved), sizeof(data.reserved)) 1ijeafbgchdnklm
216 self._ipc_descriptor = IPCEventDescriptor._init(data_b, get_event_is_blocking_sync(self._h_event)) 1ijeafbgchdnklm
217 return self._ipc_descriptor 1ijeafbgchdnklm
219 @classmethod
220 def from_ipc_descriptor(cls, ipc_descriptor: IPCEventDescriptor) -> Event:
221 """Import an event that was exported from another process.
223 Parameters
224 ----------
225 ipc_descriptor : :obj:`~_memory._ipc.IPCEventDescriptor`
226 The IPC descriptor obtained from :attr:`~Event.ipc_descriptor` in
227 another process.
229 Returns
230 -------
231 :obj:`~_event.Event`
232 A new event backed by the imported IPC handle.
234 """
235 cdef cydriver.CUipcEventHandle data
236 memcpy(data.reserved, <const void*><const char*>(ipc_descriptor._reserved), sizeof(data.reserved))
237 cdef Event self = Event.__new__(cls)
238 cdef EventHandle h_event = create_event_handle_ipc(data, ipc_descriptor._is_blocking_sync)
239 if not h_event:
240 raise RuntimeError("Failed to open IPC event handle")
241 self._h_event = h_event
242 self._ipc_descriptor = ipc_descriptor
243 return self
245 @property
246 def is_ipc_enabled(self) -> bool:
247 """Return True if the event can be shared across process boundaries, otherwise False."""
248 return get_event_ipc_enabled(self._h_event) 1oijeafbgchdnEklm
250 @property
251 def is_timing_enabled(self) -> bool:
252 """Return True if the event records timing data, otherwise False."""
253 return get_event_timing_enabled(self._h_event) 1poqeafbgchdrv
255 @property
256 def is_blocking_sync(self) -> bool:
257 """Return True if the event uses blocking synchronization (the CPU
258 thread blocks on :meth:`sync` instead of busy-waiting), otherwise False.
259 """
260 return get_event_is_blocking_sync(self._h_event) 1poqeafbgchdL
262 def sync(self) -> None:
263 """Synchronize until the event completes.
265 If the event was created with ``blocking_sync=True``, the
266 calling CPU thread blocks (yields) until the event has been
267 completed by the device. Otherwise (the default) the CPU
268 thread busy-waits until the event has completed.
270 """
271 with nogil: 1uwxGy
272 HANDLE_RETURN(cydriver.cuEventSynchronize(as_cu(self._h_event))) 1uwxGy
274 @property
275 def is_done(self) -> bool:
276 """Return True if all captured works have been completed, otherwise False."""
277 with nogil: 1pstwFx
278 result = cydriver.cuEventQuery(as_cu(self._h_event)) 1pstwFx
279 if result == cydriver.CUresult.CUDA_SUCCESS: 1pstwFx
280 return True 1pstFx
281 if result == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1w
282 return False 1w
283 HANDLE_RETURN(result)
285 @property
286 def handle(self) -> cuda.bindings.driver.CUevent:
287 """Return the underlying CUevent object.
289 .. caution::
291 This handle is a Python object. To get the memory address of the underlying C
292 handle, call ``int(Event.handle)``.
293 """
294 return as_py(self._h_event) 1ij}z
296 @property
297 def device(self) -> Device:
298 """Return the :obj:`~_device.Device` singleton associated with this event.
300 Note
301 ----
302 The current context on the device may differ from this
303 event's context. This case occurs when a different CUDA
304 context is set current after a event is created.
306 """
307 cdef int dev_id = get_event_device_id(self._h_event) 1oqC
308 if dev_id >= 0: 1oqC
309 from ._device import Device # avoid circular import 1oqC
310 return Device(dev_id) 1oqC
312 @property
313 def context(self) -> Context:
314 """Return the :obj:`~_context.Context` associated with this event."""
315 cdef ContextHandle h_ctx = get_event_context(self._h_event) 1By
316 cdef int dev_id = get_event_device_id(self._h_event) 1By
317 if h_ctx and dev_id >= 0: 1By
318 return Context._from_handle(Context, h_ctx, dev_id) 1By
321cdef class IPCEventDescriptor:
322 """Serializable object describing an event that can be shared between processes."""
324 cdef:
325 bytes _reserved
326 bint _is_blocking_sync
328 def __init__(self, *arg, **kwargs) -> None:
329 raise RuntimeError("IPCEventDescriptor objects cannot be instantiated directly. Please use Event APIs.") 2cd
331 @staticmethod
332 def _init(reserved: bytes, is_blocking_sync: cython.bint) -> IPCEventDescriptor:
333 cdef IPCEventDescriptor self = IPCEventDescriptor.__new__(IPCEventDescriptor) 2i j e a f b g c h d n _c`c{c|c}c~cadk l m
334 self._reserved = reserved 2i j e a f b g c h d n _c`c{c|c}c~cadk l m
335 self._is_blocking_sync = is_blocking_sync 2i j e a f b g c h d n _c`c{c|c}c~cadk l m
336 return self 2i j e a f b g c h d n _c`c{c|c}c~cadk l m
338 def __eq__(self, other: object) -> bool:
339 if not isinstance(other, IPCEventDescriptor): 2e a f b g c h d _c`c{c|c}c~cad
340 return NotImplemented 2_c`c{c|c}c~c
341 # No need to check self._is_blocking_sync.
342 return self._reserved == (<IPCEventDescriptor>other)._reserved 2e a f b g c h d ad
344 def __reduce__(self) -> tuple[object, ...]:
345 return IPCEventDescriptor._init, (self._reserved, self._is_blocking_sync) 1ijeafbgchdlm
348def _reduce_event(event: Event) -> tuple[object, ...]:
349 check_multiprocessing_start_method() 1ijeafbgchdk
350 return event.from_ipc_descriptor, (event.ipc_descriptor,) 1ijeafbgchdk
352multiprocessing.reduction.register(Event, _reduce_event)