Coverage for cuda/core/_event.pyx: 89.84%
128 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 01:38 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 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.stddef cimport size_t
9from libc.string cimport memcpy
10from cuda.bindings cimport cydriver
11from cuda.core._context cimport Context
12from cuda.core._resource_handles cimport (
13 ContextHandle,
14 EventHandle,
15 create_event_handle,
16 create_event_handle_ipc,
17 get_event_timing_enabled,
18 get_event_is_blocking_sync,
19 get_event_ipc_enabled,
20 get_event_device_id,
21 get_event_context,
22 as_intptr,
23 as_cu,
24 as_py,
25)
27from cuda.core._utils.cuda_utils cimport (
28 check_or_create_options,
29 HANDLE_RETURN
30)
32import cython
33from dataclasses import dataclass
34import multiprocessing
35from typing import TYPE_CHECKING
37from cuda.core._utils.cuda_utils import (
38 CUDAError,
39 check_multiprocessing_start_method,
40)
42if TYPE_CHECKING:
43 import cuda.bindings.driver # no-cython-lint
44 from cuda.core._device import Device
47@dataclass
48cdef class EventOptions:
49 """Customizable :obj:`~_event.Event` options.
51 Attributes
52 ----------
53 timing_enabled : bool, optional
54 Event will record timing data. (Default to False)
55 blocking_sync : bool, optional
56 If True, the event uses blocking synchronization: a CPU
57 thread that calls :meth:`Event.sync` blocks (yields) until
58 the event has completed. Otherwise (the default), the CPU
59 thread busy-waits until the event has completed.
60 (Default to False)
61 ipc_enabled : bool, optional
62 Event will be suitable for interprocess use.
63 Note that timing_enabled must be False. (Default to False)
65 """
67 timing_enabled: bool | None = False
68 blocking_sync: bool | None = False
69 ipc_enabled: bool | None = False
72cdef class Event:
73 """Represent a record at a specific point of execution within a CUDA stream.
75 Applications can asynchronously record events at any point in
76 the program. An event keeps a record of all previous work within
77 the last recorded stream.
79 Events can be used to monitor device's progress, query completion
80 of work up to event's record, help establish dependencies
81 between GPU work submissions, and record the elapsed time (in milliseconds)
82 on GPU:
84 .. code-block:: python
86 # To create events and record the timing:
87 s = Device().create_stream()
88 e1 = Device().create_event({"timing_enabled": True})
89 e2 = Device().create_event({"timing_enabled": True})
90 s.record(e1)
91 # ... run some GPU works ...
92 s.record(e2)
93 e2.sync()
94 print(f"time = {e2 - e1} milliseconds")
96 Directly creating an :obj:`~_event.Event` is not supported due to ambiguity,
97 and they should instead be created through a :obj:`~_stream.Stream` object.
99 """
101 def __init__(self, *args, **kwargs) -> None:
102 raise RuntimeError("Event objects cannot be instantiated directly. Please use Stream APIs (record).") 2ed
104 @staticmethod
105 cdef Event _init(type cls, int device_id, ContextHandle h_context, options, bint is_free):
106 cdef Event self = cls.__new__(cls) 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
107 cdef EventOptions opts = check_or_create_options(EventOptions, options, "Event options") 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
108 cdef unsigned int flags = 0x0 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
109 cdef bint timing_enabled = True 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
110 cdef bint is_blocking_sync = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
111 cdef bint ipc_enabled = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
112 self._ipc_descriptor = None 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
113 if not opts.timing_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
114 flags |= cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbs WbXbt q Yb~ i j e a f b g c h d n abr B ZbC bbJ cbD K E w dbebfbN L F x y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
115 timing_enabled = False 2A $ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbs WbXbt q Yb~ i j e a f b g c h d n abr B ZbC bbJ cbD K E w dbebfbN L F x y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
116 if opts.blocking_sync: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
117 flags |= cydriver.CUevent_flags.CU_EVENT_BLOCKING_SYNC 1poabcdL
118 is_blocking_sync = True 1poabcdL
119 if opts.ipc_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n @c[cabr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
120 if is_free: 2i j e a f b g c h d n @c[ck l m
121 raise TypeError( 1n
122 "IPC-enabled events must be bound; use Stream.record for creation."
123 )
124 flags |= cydriver.CUevent_flags.CU_EVENT_INTERPROCESS 2i j e a f b g c h d n @c[ck l m
125 ipc_enabled = True 2i j e a f b g c h d n @c[ck l m
126 if timing_enabled: 2i j e a f b g c h d n @c[ck l m
127 raise TypeError("IPC-enabled events cannot use timing.") 2@c[c
128 cdef EventHandle h_event = create_event_handle( 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n abr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
129 h_context, flags, timing_enabled, is_blocking_sync, ipc_enabled, device_id)
130 if not h_event: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n abr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
131 raise RuntimeError("Failed to create CUDA event")
132 self._h_event = h_event 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n abr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
133 if ipc_enabled: 2A $ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n abr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
134 _ = self.ipc_descriptor # eagerly populate the descriptor cache 1ijeafbgchdnklm
135 return self 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { | } JbKbLbMbNbObPbQbRbSbTbUbVbp s o WbXbt q Yb~ i j e a f b g c h d n abr u v B ZbC bbJ cbD K E w dbebfbN L F x G y k z 0b1bgb2b3b4b5b6b7b8b9b!b#b$b%bhb'b(b)b*bibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDb+b,b-b.b/b:b;b=b?b@b[b]b^b_b`b{b|b}b~bacbcccdcecfcgchcicjcEbkclcFbmcncocpcqcrcsctcucvcwcxcO yczcAcBcP Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! CcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c3c4c# 5c6cl m Gb7c8cM 9c!cHb#c$cIb%c'cH (c)cI *c+c,c-c.c/c:c;c=c?c
137 @staticmethod
138 cdef Event _from_handle(EventHandle h_event):
139 """Create an Event wrapping an existing EventHandle.
141 Metadata (timing, blocking_sync, ipc, device_id) is read from
142 the EventBox via pointer arithmetic — no fields are cached on
143 Event.
144 """
145 cdef Event self = Event.__new__(Event) 2^c_c`c{cp s o t q
146 self._h_event = h_event 2^c_c`c{cp s o t q
147 self._ipc_descriptor = None 2^c_c`c{cp s o t q
148 return self 2^c_c`c{cp s o t q
150 cpdef close(self):
151 """Destroy the event.
153 Releases the event handle. The underlying CUDA event is destroyed
154 when the last reference is released.
155 """
156 self._h_event.reset() 1$%'()*+,-./:;=?@[]^_`{|}~k
158 def __isub__(self, other: object):
159 return NotImplemented 2db
161 def __rsub__(self, other: object):
162 return NotImplemented 2eb
164 def __sub__(self, other: Event) -> float:
165 # return self - other (in milliseconds)
166 cdef float timing
167 with nogil: 1ruvG
168 err = cydriver.cuEventElapsedTime(&timing, as_cu((<Event>other)._h_event), as_cu(self._h_event)) 1ruvG
169 if err == 0: 1ruvG
170 return timing 1uG
171 else:
172 if err == cydriver.CUresult.CUDA_ERROR_INVALID_HANDLE: 1ruv
173 if not self.is_timing_enabled or not other.is_timing_enabled: 1rv
174 explanation = (
175 "Both Events must be created with timing enabled in order to subtract them; " 1r
176 "use EventOptions(timing_enabled=True) when creating both events."
177 )
178 else:
179 explanation = (
180 "Both Events must be recorded before they can be subtracted; " 1v
181 "use Stream.record() to record both events to a stream."
182 )
183 elif err == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1ruv
184 explanation = (
185 "One or both events have not completed; " 1u
186 "use Event.sync(), Stream.sync(), or Device.sync() to wait for the events to complete "
187 "before subtracting them."
188 )
189 else:
190 raise CUDAError(err)
191 raise RuntimeError(explanation) 1ruv
193 def __hash__(self) -> int:
194 return hash(as_intptr(self._h_event)) 2bbcbD fbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbHbIbH I
196 def __eq__(self, other: object) -> bool:
197 # Note: using isinstance because `Event` can be subclassed.
198 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
199 return NotImplemented 1NzOPQRSTUVWXYZ0123456789!#
200 cdef Event _other = <Event>other 2^c_c`c{cJ D K z M H I
201 return as_intptr(self._h_event) == as_intptr(_other._h_event) 2^c_c`c{cJ D K z M H I
203 def __repr__(self) -> str:
204 return f"<Event handle={as_intptr(self._h_event):#x}>" 2i j e a f b g c h d Gb
206 @property
207 def ipc_descriptor(self) -> IPCEventDescriptor:
208 """Descriptor for sharing this event with other processes."""
209 if self._ipc_descriptor is not None: 1ijeafbgchdnEklm
210 return self._ipc_descriptor 1ijeafbgchdklm
211 if not self.is_ipc_enabled: 1ijeafbgchdnEklm
212 raise RuntimeError("Event is not IPC-enabled") 1E
213 cdef cydriver.CUipcEventHandle data
214 with nogil: 1ijeafbgchdnklm
215 HANDLE_RETURN(cydriver.cuIpcGetEventHandle(&data, as_cu(self._h_event))) 1ijeafbgchdnklm
216 cdef bytes data_b = cpython.PyBytes_FromStringAndSize(<char*>(data.reserved), sizeof(data.reserved)) 1ijeafbgchdnklm
217 self._ipc_descriptor = IPCEventDescriptor._init(data_b, get_event_is_blocking_sync(self._h_event)) 1ijeafbgchdnklm
218 return self._ipc_descriptor 1ijeafbgchdnklm
220 @classmethod
221 def from_ipc_descriptor(cls, ipc_descriptor: IPCEventDescriptor) -> Event:
222 """Import an event that was exported from another process.
224 Parameters
225 ----------
226 ipc_descriptor : :obj:`~_memory._ipc.IPCEventDescriptor`
227 The IPC descriptor obtained from :attr:`~Event.ipc_descriptor` in
228 another process.
230 Returns
231 -------
232 :obj:`~_event.Event`
233 A new event backed by the imported IPC handle.
235 """
236 cdef size_t reserved_size = len(ipc_descriptor._reserved) 2]c
237 cdef size_t expected_size = sizeof(cydriver.CUipcEventHandle) 2]c
238 if reserved_size < expected_size: 2]c
239 raise ValueError( 2]c
240 f"IPC event descriptor reserved field is {reserved_size} bytes; " 2]c
241 f"expected at least {expected_size}" 2]c
242 )
243 cdef cydriver.CUipcEventHandle data
244 memcpy(data.reserved, <const void*><const char*>(ipc_descriptor._reserved), sizeof(data.reserved))
245 cdef Event self = Event.__new__(cls)
246 cdef EventHandle h_event = create_event_handle_ipc(data, ipc_descriptor._is_blocking_sync)
247 if not h_event:
248 raise RuntimeError("Failed to open IPC event handle")
249 self._h_event = h_event
250 self._ipc_descriptor = ipc_descriptor
251 return self
253 @property
254 def is_ipc_enabled(self) -> bool:
255 """Return True if the event can be shared across process boundaries, otherwise False."""
256 return get_event_ipc_enabled(self._h_event) 1oijeafbgchdnEklm
258 @property
259 def is_timing_enabled(self) -> bool:
260 """Return True if the event records timing data, otherwise False."""
261 return get_event_timing_enabled(self._h_event) 1poqeafbgchdrv
263 @property
264 def is_blocking_sync(self) -> bool:
265 """Return True if the event uses blocking synchronization (the CPU
266 thread blocks on :meth:`sync` instead of busy-waiting), otherwise False.
267 """
268 return get_event_is_blocking_sync(self._h_event) 1poqeafbgchdL
270 def sync(self) -> None:
271 """Synchronize until the event completes.
273 If the event was created with ``blocking_sync=True``, the
274 calling CPU thread blocks (yields) until the event has been
275 completed by the device. Otherwise (the default) the CPU
276 thread busy-waits until the event has completed.
278 """
279 with nogil: 1uwxGy
280 HANDLE_RETURN(cydriver.cuEventSynchronize(as_cu(self._h_event))) 1uwxGy
282 @property
283 def is_done(self) -> bool:
284 """Return True if all captured works have been completed, otherwise False."""
285 with nogil: 1pstwFx
286 result = cydriver.cuEventQuery(as_cu(self._h_event)) 1pstwFx
287 if result == cydriver.CUresult.CUDA_SUCCESS: 1pstwFx
288 return True 1pstFx
289 if result == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1w
290 return False 1w
291 HANDLE_RETURN(result)
293 @property
294 def handle(self) -> cuda.bindings.driver.CUevent:
295 """Return the underlying CUevent object.
297 .. caution::
299 This handle is a Python object. To get the memory address of the underlying C
300 handle, call ``int(Event.handle)``.
301 """
302 return as_py(self._h_event) 2i j abz
304 @property
305 def device(self) -> Device:
306 """Return the :obj:`~_device.Device` singleton associated with this event.
308 Note
309 ----
310 The current context on the device may differ from this
311 event's context. This case occurs when a different CUDA
312 context is set current after a event is created.
314 """
315 cdef int dev_id = get_event_device_id(self._h_event) 1oqC
316 if dev_id >= 0: 1oqC
317 from ._device import Device # avoid circular import 1oqC
318 return Device(dev_id) 1oqC
320 @property
321 def context(self) -> Context:
322 """Return the :obj:`~_context.Context` associated with this event."""
323 cdef ContextHandle h_ctx = get_event_context(self._h_event) 1By
324 cdef int dev_id = get_event_device_id(self._h_event) 1By
325 if h_ctx and dev_id >= 0: 1By
326 return Context._from_handle(Context, h_ctx, dev_id) 1By
329cdef class IPCEventDescriptor:
330 """Serializable object describing an event that can be shared between processes."""
332 cdef:
333 bytes _reserved
334 bint _is_blocking_sync
336 def __init__(self, *arg, **kwargs) -> None:
337 raise RuntimeError("IPCEventDescriptor objects cannot be instantiated directly. Please use Event APIs.") 2fd
339 @staticmethod
340 def _init(reserved: bytes, is_blocking_sync: cython.bint) -> IPCEventDescriptor:
341 cdef IPCEventDescriptor self = IPCEventDescriptor.__new__(IPCEventDescriptor) 2i j e a f b g c h d n ]c|c}c~cadbdcdddk l m
342 self._reserved = reserved 2i j e a f b g c h d n ]c|c}c~cadbdcdddk l m
343 self._is_blocking_sync = is_blocking_sync 2i j e a f b g c h d n ]c|c}c~cadbdcdddk l m
344 return self 2i j e a f b g c h d n ]c|c}c~cadbdcdddk l m
346 def __eq__(self, other: object) -> bool:
347 if not isinstance(other, IPCEventDescriptor): 2e a f b g c h d |c}c~cadbdcddd
348 return NotImplemented 2|c}c~cadbdcd
349 # No need to check self._is_blocking_sync.
350 return self._reserved == (<IPCEventDescriptor>other)._reserved 2e a f b g c h d dd
352 def __reduce__(self) -> tuple[object, ...]:
353 return IPCEventDescriptor._init, (self._reserved, self._is_blocking_sync) 1ijeafbgchdlm
356def _reduce_event(event: Event) -> tuple[object, ...]:
357 check_multiprocessing_start_method() 1ijeafbgchdk
358 return event.from_ipc_descriptor, (event.ipc_descriptor,) 1ijeafbgchdk
360multiprocessing.reduction.register(Event, _reduce_event)