Coverage for cuda / core / _event.pyx: 90.76%

119 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-22 01:37 +0000

1# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from __future__ import annotations 

6  

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) 

25  

26from cuda.core._utils.cuda_utils cimport ( 

27 check_or_create_options, 

28 HANDLE_RETURN 

29) 

30  

31import cython 

32from dataclasses import dataclass 

33import multiprocessing 

34  

35from cuda.core._utils.cuda_utils import ( 

36 CUDAError, 

37 check_multiprocessing_start_method, 

38) 

39  

40  

41@dataclass 

42cdef class EventOptions: 

43 """Customizable :obj:`~_event.Event` options. 

44  

45 Attributes 

46 ---------- 

47 timing_enabled : bool, optional 

48 Event will record timing data. (Default to False) 

49 blocking_sync : bool, optional 

50 If True, the event uses blocking synchronization: a CPU 

51 thread that calls :meth:`Event.sync` blocks (yields) until 

52 the event has completed. Otherwise (the default), the CPU 

53 thread busy-waits until the event has completed. 

54 (Default to False) 

55 ipc_enabled : bool, optional 

56 Event will be suitable for interprocess use. 

57 Note that timing_enabled must be False. (Default to False) 

58  

59 """ 

60  

61 timing_enabled: bool | None = False 

62 blocking_sync: bool | None = False 

63 ipc_enabled: bool | None = False 

64  

65  

66cdef class Event: 

67 """Represent a record at a specific point of execution within a CUDA stream. 

68  

69 Applications can asynchronously record events at any point in 

70 the program. An event keeps a record of all previous work within 

71 the last recorded stream. 

72  

73 Events can be used to monitor device's progress, query completion 

74 of work up to event's record, help establish dependencies 

75 between GPU work submissions, and record the elapsed time (in milliseconds) 

76 on GPU: 

77  

78 .. code-block:: python 

79  

80 # To create events and record the timing: 

81 s = Device().create_stream() 

82 e1 = Device().create_event({"timing_enabled": True}) 

83 e2 = Device().create_event({"timing_enabled": True}) 

84 s.record(e1) 

85 # ... run some GPU works ... 

86 s.record(e2) 

87 e2.sync() 

88 print(f"time = {e2 - e1} milliseconds") 

89  

90 Directly creating an :obj:`~_event.Event` is not supported due to ambiguity, 

91 and they should instead be created through a :obj:`~_stream.Stream` object. 

92  

93 """ 

94  

95 def __init__(self, *args, **kwargs): 

96 raise RuntimeError("Event objects cannot be instantiated directly. Please use Stream APIs (record).") 2bd

97  

98 @staticmethod 

99 cdef Event _init(type cls, int device_id, ContextHandle h_context, options, bint is_free): 

100 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

101 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

102 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

103 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

104 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

105 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

106 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

107 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

108 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 A XbB ~ I abC J D w bbcbdbM K E 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

109 timing_enabled = False 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbs UbVbt q Wb| i j e a f b g c h d n } r A XbB ~ I abC J D w bbcbdbM K E 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

110 if opts.blocking_sync: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n =c?c} r u v A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

111 flags |= cydriver.CUevent_flags.CU_EVENT_BLOCKING_SYNC 1poabcdK

112 is_blocking_sync = True 1poabcdK

113 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

114 if is_free: 2i j e a f b g c h d n =c?ck l m

115 raise TypeError( 1#n

116 "IPC-enabled events must be bound; use Stream.record for creation." 

117 ) 

118 flags |= cydriver.CUevent_flags.CU_EVENT_INTERPROCESS 2i j e a f b g c h d n =c?ck l m

119 ipc_enabled = True 2i j e a f b g c h d n =c?ck l m

120 if timing_enabled: 2i j e a f b g c h d n =c?ck l m

121 raise TypeError("IPC-enabled events cannot use timing.") 2=c?c

122 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

123 h_context, flags, timing_enabled, is_blocking_sync, ipc_enabled, device_id) 

124 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

125 raise RuntimeError("Failed to create CUDA event") 

126 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 A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

127 if ipc_enabled: 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

128 _ = self.ipc_descriptor # eagerly populate the descriptor cache 1ijeafbgchdnklm

129 return self 2$ % ' ( ) * + , - . / : ; = ? @ [ ] ^ _ ` { HbIbJbKbLbMbNbObPbQbRbSbTbp s o UbVbt q Wb| i j e a f b g c h d n } r u v A XbB ~ I abC J D w bbcbdbM K E x F 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~bacbcccdcecfcgchcCbicjcDbkclcmcncocpcqcrcsctcucvcN wcxcyczcO P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc0c1c2c! 3c4cl m Eb5c6cL 7c8cFb9c!cGb#c$cG %c'cH (c)c*c+c,c-c.c/c:c;c

130  

131 @staticmethod 

132 cdef Event _from_handle(EventHandle h_event): 

133 """Create an Event wrapping an existing EventHandle. 

134  

135 Metadata (timing, blocking_sync, ipc, device_id) is read from 

136 the EventBox via pointer arithmetic — no fields are cached on 

137 Event. 

138 """ 

139 cdef Event self = Event.__new__(Event) 2@c[c]c^cp s o t q

140 self._h_event = h_event 2@c[c]c^cp s o t q

141 self._ipc_descriptor = None 2@c[c]c^cp s o t q

142 return self 2@c[c]c^cp s o t q

143  

144 cpdef close(self): 

145 """Destroy the event. 

146  

147 Releases the event handle. The underlying CUDA event is destroyed 

148 when the last reference is released. 

149 """ 

150 self._h_event.reset() 1$%'()*+,-./:;=?@[]^_`{|k

151  

152 def __isub__(self, other): 

153 return NotImplemented 2bb

154  

155 def __rsub__(self, other): 

156 return NotImplemented 2cb

157  

158 def __sub__(self, other: Event): 

159 # return self - other (in milliseconds) 

160 cdef float timing 

161 with nogil: 1ruvF

162 err = cydriver.cuEventElapsedTime(&timing, as_cu((<Event>other)._h_event), as_cu(self._h_event)) 1ruvF

163 if err == 0: 1ruvF

164 return timing 1uF

165 else: 

166 if err == cydriver.CUresult.CUDA_ERROR_INVALID_HANDLE: 1ruv

167 if not self.is_timing_enabled or not other.is_timing_enabled: 1rv

168 explanation = ( 

169 "Both Events must be created with timing enabled in order to subtract them; " 1r

170 "use EventOptions(timing_enabled=True) when creating both events." 

171 ) 

172 else: 

173 explanation = ( 

174 "Both Events must be recorded before they can be subtracted; " 1v

175 "use Stream.record() to record both events to a stream." 

176 ) 

177 elif err == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1ruv

178 explanation = ( 

179 "One or both events have not completed; " 1u

180 "use Event.sync(), Stream.sync(), or Device.sync() to wait for the events to complete " 

181 "before subtracting them." 

182 ) 

183 else: 

184 raise CUDAError(err) 

185 raise RuntimeError(explanation) 1ruv

186  

187 def __hash__(self) -> int: 

188 return hash(as_intptr(self._h_event)) 2~ abC dbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbFbGbG H

189  

190 def __eq__(self, other) -> bool: 

191 # Note: using isinstance because `Event` can be subclassed. 

192 if not isinstance(other, Event): 2@c[c]c^cI C J M z N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! L G H

193 return NotImplemented 1MzNOPQRSTUVWXYZ0123456789!

194 cdef Event _other = <Event>other 2@c[c]c^cI C J z L G H

195 return as_intptr(self._h_event) == as_intptr(_other._h_event) 2@c[c]c^cI C J z L G H

196  

197 def __repr__(self) -> str: 

198 return f"<Event handle={as_intptr(self._h_event):#x}>" 2i j e a f b g c h d Eb

199  

200 @property 

201 def ipc_descriptor(self) -> IPCEventDescriptor: 

202 """Descriptor for sharing this event with other processes.""" 

203 if self._ipc_descriptor is not None: 1ijeafbgchdnDklm

204 return self._ipc_descriptor 1ijeafbgchdklm

205 if not self.is_ipc_enabled: 1ijeafbgchdnDklm

206 raise RuntimeError("Event is not IPC-enabled") 1D

207 cdef cydriver.CUipcEventHandle data 

208 with nogil: 1ijeafbgchdnklm

209 HANDLE_RETURN(cydriver.cuIpcGetEventHandle(&data, as_cu(self._h_event))) 1ijeafbgchdnklm

210 cdef bytes data_b = cpython.PyBytes_FromStringAndSize(<char*>(data.reserved), sizeof(data.reserved)) 1ijeafbgchdnklm

211 self._ipc_descriptor = IPCEventDescriptor._init(data_b, get_event_is_blocking_sync(self._h_event)) 1ijeafbgchdnklm

212 return self._ipc_descriptor 1ijeafbgchdnklm

213  

214 @classmethod 

215 def from_ipc_descriptor(cls, ipc_descriptor: IPCEventDescriptor) -> Event: 

216 """Import an event that was exported from another process. 

217  

218 Parameters 

219 ---------- 

220 ipc_descriptor : :obj:`~_memory._ipc.IPCEventDescriptor` 

221 The IPC descriptor obtained from :attr:`~Event.ipc_descriptor` in 

222 another process. 

223  

224 Returns 

225 ------- 

226 :obj:`~_event.Event` 

227 A new event backed by the imported IPC handle. 

228  

229 """ 

230 cdef cydriver.CUipcEventHandle data 

231 memcpy(data.reserved, <const void*><const char*>(ipc_descriptor._reserved), sizeof(data.reserved)) 

232 cdef Event self = Event.__new__(cls) 

233 cdef EventHandle h_event = create_event_handle_ipc(data, ipc_descriptor._is_blocking_sync) 

234 if not h_event: 

235 raise RuntimeError("Failed to open IPC event handle") 

236 self._h_event = h_event 

237 self._ipc_descriptor = ipc_descriptor 

238 return self 

239  

240 @property 

241 def is_ipc_enabled(self) -> bool: 

242 """Return True if the event can be shared across process boundaries, otherwise False.""" 

243 return get_event_ipc_enabled(self._h_event) 1oijeafbgchdnDklm

244  

245 @property 

246 def is_timing_enabled(self) -> bool: 

247 """Return True if the event records timing data, otherwise False.""" 

248 return get_event_timing_enabled(self._h_event) 1poqeafbgchdrv

249  

250 @property 

251 def is_blocking_sync(self) -> bool: 

252 """Return True if the event uses blocking synchronization (the CPU 

253 thread blocks on :meth:`sync` instead of busy-waiting), otherwise False. 

254 """ 

255 return get_event_is_blocking_sync(self._h_event) 1poqeafbgchdK

256  

257 def sync(self): 

258 """Synchronize until the event completes. 

259  

260 If the event was created with ``blocking_sync=True``, the 

261 calling CPU thread blocks (yields) until the event has been 

262 completed by the device. Otherwise (the default) the CPU 

263 thread busy-waits until the event has completed. 

264  

265 """ 

266 with nogil: 1uwxFy

267 HANDLE_RETURN(cydriver.cuEventSynchronize(as_cu(self._h_event))) 1uwxFy

268  

269 @property 

270 def is_done(self) -> bool: 

271 """Return True if all captured works have been completed, otherwise False.""" 

272 with nogil: 1pstwEx

273 result = cydriver.cuEventQuery(as_cu(self._h_event)) 1pstwEx

274 if result == cydriver.CUresult.CUDA_SUCCESS: 1pstwEx

275 return True 1pstEx

276 if result == cydriver.CUresult.CUDA_ERROR_NOT_READY: 1w

277 return False 1w

278 HANDLE_RETURN(result) 

279  

280 @property 

281 def handle(self) -> cuda.bindings.driver.CUevent: 

282 """Return the underlying CUevent object. 

283  

284 .. caution:: 

285  

286 This handle is a Python object. To get the memory address of the underlying C 

287 handle, call ``int(Event.handle)``. 

288 """ 

289 return as_py(self._h_event) 1ij}z

290  

291 @property 

292 def device(self) -> Device: 

293 """Return the :obj:`~_device.Device` singleton associated with this event. 

294  

295 Note 

296 ---- 

297 The current context on the device may differ from this 

298 event's context. This case occurs when a different CUDA 

299 context is set current after a event is created. 

300  

301 """ 

302 cdef int dev_id = get_event_device_id(self._h_event) 1oqB

303 if dev_id >= 0: 1oqB

304 from ._device import Device # avoid circular import 1oqB

305 return Device(dev_id) 1oqB

306  

307 @property 

308 def context(self) -> Context: 

309 """Return the :obj:`~_context.Context` associated with this event.""" 

310 cdef ContextHandle h_ctx = get_event_context(self._h_event) 1Ay

311 cdef int dev_id = get_event_device_id(self._h_event) 1Ay

312 if h_ctx and dev_id >= 0: 1Ay

313 return Context._from_handle(Context, h_ctx, dev_id) 1Ay

314  

315  

316cdef class IPCEventDescriptor: 

317 """Serializable object describing an event that can be shared between processes.""" 

318  

319 cdef: 

320 bytes _reserved 

321 bint _is_blocking_sync 

322  

323 def __init__(self, *arg, **kwargs): 

324 raise RuntimeError("IPCEventDescriptor objects cannot be instantiated directly. Please use Event APIs.") 2cd

325  

326 @staticmethod 

327 def _init(reserved: bytes, is_blocking_sync: cython.bint): 

328 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

329 self._reserved = reserved 2i j e a f b g c h d n _c`c{c|c}c~cadk l m

330 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

331 return self 2i j e a f b g c h d n _c`c{c|c}c~cadk l m

332  

333 def __eq__(self, other) -> bool: 

334 if not isinstance(other, IPCEventDescriptor): 2e a f b g c h d _c`c{c|c}c~cad

335 return NotImplemented 2_c`c{c|c}c~c

336 # No need to check self._is_blocking_sync. 

337 return self._reserved == (<IPCEventDescriptor>other)._reserved 2e a f b g c h d ad

338  

339 def __reduce__(self): 

340 return IPCEventDescriptor._init, (self._reserved, self._is_blocking_sync) 1ijeafbgchdlm

341  

342  

343def _reduce_event(event): 

344 check_multiprocessing_start_method() 1ijeafbgchdk

345 return event.from_ipc_descriptor, (event.ipc_descriptor,) 1ijeafbgchdk

346  

347multiprocessing.reduction.register(Event, _reduce_event)