Skip to content

Commit 3d14012

Browse files
committed
Broadcast: Use hash() instead of a UUID
UUIDs are expensive to create and not really necessary in this case as we are just keeping a local list of unique objects, so using the object's hash is enough. We also use a `id(self)`-based name by default if a `name` was not provided when creating a `new_receiver()`. A simple test using `timeit` shows a 2 orders of magnitude improvement in Python 3.11: ```console $ python -m timeit -s "import uuid" "uuid.uuid4()" 100000 loops, best of 5: 2.67 usec per loop $ python -m timeit "hash(__name__)" 10000000 loops, best of 5: 19.6 nsec per loop $ python -m timeit "id(__name__)" 10000000 loops, best of 5: 20.1 nsec per loop ``` Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 2a03e51 commit 3d14012

File tree

1 file changed

+17
-22
lines changed

1 file changed

+17
-22
lines changed

src/frequenz/channels/_broadcast.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from asyncio import Condition
1111
from collections import deque
1212
from typing import Generic
13-
from uuid import UUID, uuid4
1413

1514
from ._base_classes import Peekable as BasePeekable
1615
from ._base_classes import Receiver as BaseReceiver
@@ -109,8 +108,8 @@ def __init__(self, *, name: str | None, resend_latest: bool = False) -> None:
109108
self._recv_cv: Condition = Condition()
110109
"""The condition to wait for data in the channel's buffer."""
111110

112-
self._receivers: dict[UUID, weakref.ReferenceType[Receiver[T]]] = {}
113-
"""The receivers attached to the channel, indexed by their UUID."""
111+
self._receivers: dict[int, weakref.ReferenceType[Receiver[T]]] = {}
112+
"""The receivers attached to the channel, indexed by their hash()."""
114113

115114
self._closed: bool = False
116115
"""Whether the channel is closed."""
@@ -183,11 +182,8 @@ def new_receiver(self, *, name: str | None = None, limit: int = 50) -> Receiver[
183182
Returns:
184183
A Receiver instance attached to the broadcast channel.
185184
"""
186-
uuid = uuid4()
187-
if name is None:
188-
name = str(uuid)
189-
recv: Receiver[T] = Receiver(uuid, name, limit, self)
190-
self._receivers[uuid] = weakref.ref(recv)
185+
recv: Receiver[T] = Receiver(name, limit, self)
186+
self._receivers[hash(recv)] = weakref.ref(recv)
191187
if self.resend_latest and self._latest is not None:
192188
recv.enqueue(self._latest)
193189
return recv
@@ -240,14 +236,14 @@ async def send(self, msg: T) -> None:
240236
)
241237
self._chan._latest = msg
242238
stale_refs = []
243-
for name, recv_ref in self._chan._receivers.items():
239+
for _hash, recv_ref in self._chan._receivers.items():
244240
recv = recv_ref()
245241
if recv is None:
246-
stale_refs.append(name)
242+
stale_refs.append(_hash)
247243
continue
248244
recv.enqueue(msg)
249-
for name in stale_refs:
250-
del self._chan._receivers[name]
245+
for _hash in stale_refs:
246+
del self._chan._receivers[_hash]
251247
async with self._chan._recv_cv:
252248
self._chan._recv_cv.notify_all()
253249
# pylint: enable=protected-access
@@ -261,25 +257,23 @@ class Receiver(BaseReceiver[T]):
261257
method.
262258
"""
263259

264-
def __init__(self, uuid: UUID, name: str, limit: int, chan: Broadcast[T]) -> None:
260+
def __init__(self, name: str | None, limit: int, chan: Broadcast[T]) -> None:
265261
"""Create a broadcast receiver.
266262
267263
Broadcast receivers have their own buffer, and when messages are not
268264
being consumed fast enough and the buffer fills up, old messages will
269265
get dropped just in this receiver.
270266
271267
Args:
272-
uuid: A uuid to identify the receiver in the broadcast channel's
273-
list of receivers.
274-
name: A name to identify the receiver in the logs.
268+
name: A name to identify the receiver in the logs. If `None` an
269+
`id(self)`-based name will be used. This is only for debugging
270+
purposes, it will be shown in the string representation of the
271+
receiver.
275272
limit: Size of the receiver's buffer in number of messages.
276273
chan: a reference to the Broadcast channel that this receiver
277274
belongs to.
278275
"""
279-
self._uuid: UUID = uuid
280-
"""The UUID to identify the receiver in the broadcast channel's list of receivers."""
281-
282-
self._name: str = name
276+
self._name: str = name if name is not None else f"{id(self):_}"
283277
"""The name to identify the receiver.
284278
285279
Only used for debugging purposes.
@@ -361,8 +355,9 @@ def _deactivate(self) -> None:
361355
"""Set the receiver as inactive and remove it from the channel."""
362356
self._active = False
363357
# pylint: disable=protected-access
364-
if self._uuid in self._chan._receivers:
365-
del self._chan._receivers[self._uuid]
358+
_hash = hash(self)
359+
if _hash in self._chan._receivers:
360+
del self._chan._receivers[_hash]
366361
# pylint: enable=protected-access
367362

368363
def consume(self) -> T:

0 commit comments

Comments
 (0)