[Django] #27513: Optimize Signal.send a tiny bit

15 views
Skip to first unread message

Django

unread,
Nov 20, 2016, 8:37:22 AM11/20/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
------------------------------------------------+------------------------
Reporter: Adam Chainz | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Utilities | Version: 1.10
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
------------------------------------------------+------------------------
Signals often have no listeners so their `send()` is a no-op. #16639 tried
to optimize the model init signals to be faster for this case, but came
out too complicated.

One micro optimization can be done in the current no-op code is to avoid
assigning the empty list to a variable before returning it, which is less
operations in the Python virtual machine, for example:

{{{
In [5]: import dis

In [6]: def foo1():
...: responses = []
...: if True:
...: return responses
...:

In [7]: dis.dis(foo1)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (responses)

3 6 LOAD_GLOBAL 0 (True)
9 POP_JUMP_IF_FALSE 16

4 12 LOAD_FAST 0 (responses)
15 RETURN_VALUE
>> 16 LOAD_CONST 0 (None)
19 RETURN_VALUE

In [8]: def foo2():
...: if True:
...: return []
...:

In [9]: dis.dis(foo2)
2 0 LOAD_GLOBAL 0 (True)
3 POP_JUMP_IF_FALSE 10

3 6 BUILD_LIST 0
9 RETURN_VALUE
>> 10 LOAD_CONST 0 (None)
13 RETURN_VALUE
}}}

Timing the two above example functions gives:

{{{
In [13]: %timeit foo1()
10000000 loops, best of 3: 160 ns per loop

In [14]: %timeit foo2()
10000000 loops, best of 3: 121 ns per loop
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/27513>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Nov 20, 2016, 8:38:33 AM11/20/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
-------------------------------------+-------------------------------------
Reporter: Adam Chainz | Owner: Adam
Type: | Chainz
Cleanup/optimization | Status: assigned
Component: Utilities | Version: 1.10
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Adam Chainz):

* owner: nobody => Adam Chainz
* status: new => assigned
* has_patch: 0 => 1


Comment:

https://github.com/django/django/pull/7581

--
Ticket URL: <https://code.djangoproject.com/ticket/27513#comment:1>

Django

unread,
Nov 20, 2016, 8:56:16 AM11/20/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
-------------------------------------+-------------------------------------
Reporter: Adam Chainz | Owner: Adam
Type: | Chainz
Cleanup/optimization | Status: assigned
Component: Utilities | Version: 1.10
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Adam Chainz):

* cc: me@… (added)


--
Ticket URL: <https://code.djangoproject.com/ticket/27513#comment:2>

Django

unread,
Nov 20, 2016, 9:23:54 AM11/20/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
-------------------------------------+-------------------------------------
Reporter: Adam Chainz | Owner: Adam
Type: | Chainz
Cleanup/optimization | Status: assigned
Component: Utilities | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Markus Holtermann):

* version: 1.10 => master
* stage: Unreviewed => Ready for checkin


Comment:

LGTM

--
Ticket URL: <https://code.djangoproject.com/ticket/27513#comment:3>

Django

unread,
Nov 20, 2016, 10:32:07 AM11/20/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
-------------------------------------+-------------------------------------
Reporter: Adam Chainz | Owner: Adam
Type: | Chainz
Cleanup/optimization | Status: assigned
Component: Utilities | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Adam Chainz):

`send()` with receivers can be optimized too with a list comprehension,
avoiding temp var `response` and method calls on `responses`:

{{{
In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def before(self, sender, **named):
for receiver in self._live_receivers(sender):
response = receiver(signal=self, sender=sender, **named)
responses.append((receiver, response))
return responses


def after(self, sender, **named):
return [
(receiver, receiver(signal=self, sender=sender, **named))
for receiver in self._live_receivers(sender)
]
:<EOF>

In [3]: dis.dis(before)
2 0 SETUP_LOOP 66 (to 69)
3 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (_live_receivers)
9 LOAD_FAST 1 (sender)
12 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
15 GET_ITER
>> 16 FOR_ITER 49 (to 68)
19 STORE_FAST 3 (receiver)

3 22 LOAD_FAST 3 (receiver)
25 LOAD_CONST 1 ('signal')
28 LOAD_FAST 0 (self)
31 LOAD_CONST 2 ('sender')
34 LOAD_FAST 1 (sender)
37 LOAD_FAST 2 (named)
40 CALL_FUNCTION_KW 512 (0 positional, 2 keyword pair)
43 STORE_FAST 4 (response)

4 46 LOAD_GLOBAL 1 (responses)
49 LOAD_ATTR 2 (append)
52 LOAD_FAST 3 (receiver)
55 LOAD_FAST 4 (response)
58 BUILD_TUPLE 2
61 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
64 POP_TOP
65 JUMP_ABSOLUTE 16
>> 68 POP_BLOCK

5 >> 69 LOAD_GLOBAL 1 (responses)
72 RETURN_VALUE

In [4]: dis.dis(after)
10 0 LOAD_CLOSURE 0 (named)
3 LOAD_CLOSURE 1 (self)
6 LOAD_CLOSURE 2 (sender)
9 BUILD_TUPLE 3
12 LOAD_CONST 1 (<code object <listcomp> at
0x1068b5930, file "<ipython-input-1-cece5b56e5de>", line 10>)
15 LOAD_CONST 2 ('after.<locals>.<listcomp>')
18 MAKE_CLOSURE 0

11 21 LOAD_DEREF 1 (self)
24 LOAD_ATTR 0 (_live_receivers)
27 LOAD_DEREF 2 (sender)
30 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
33 GET_ITER
34 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
37 RETURN_VALUE
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/27513#comment:4>

Django

unread,
Nov 22, 2016, 9:30:37 AM11/22/16
to django-...@googlegroups.com
#27513: Optimize Signal.send a tiny bit
-------------------------------------+-------------------------------------
Reporter: Adam Chainz | Owner: Adam
Type: | Chainz
Cleanup/optimization | Status: closed
Component: Utilities | Version: master
Severity: Normal | Resolution: fixed

Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham <timograham@…>):

* status: assigned => closed
* resolution: => fixed


Comment:

In [changeset:"22a60f8d0b331bf06c066ccba4eea5bb5e4ac9f2" 22a60f8d]:
{{{
#!CommitTicketReference repository=""
revision="22a60f8d0b331bf06c066ccba4eea5bb5e4ac9f2"
Fixed #27513 -- Made Signal.send()/send_robust() a tiny bit faster.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/27513#comment:5>

Reply all
Reply to author
Forward
0 new messages