8000 Possible easy performance improvement: tuple/list literals -> set literals · Issue #7737 · matrix-org/synapse · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Possible easy performance improvement: tuple/list literals -> set literals #7737
@joepie91

Description

@joepie91

Description

In many places in the Synapse code, some variation of the following code exists:

if foo in ("bar", "baz", "qux"):
# ... or ...
if foo in ["bar", "baz", "qux"]:
# ... or ...
STUFF = ["bar", "baz", "qux"]
if foo in STUFF:

These use tuple and list literals respectively, while the resulting tuples/lists are only ever used for presence checking of particular entries. Some quick microbenchmarking suggested, however, that set literals would be significantly faster here (relatively speaking), for both hits and misses:

$ python3 -m timeit '"nyet" in { "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 }'
10000000 loops, best of 5: 21.8 nsec per loop
$ python3 -m timeit '"baz" in { "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 }'
20000000 loops, best of 5: 18.1 nsec per loop

$ python3 -m timeit '"nyet" in [ "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]'
1000000 loops, best of 5: 276 nsec per loop
$ python3 -m timeit '"baz" in [ "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]'
5000000 loops, best of 5: 54 nsec per loop

$ python3 -m timeit '"nyet" in ( "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 )'
1000000 loops, best of 5: 264 nsec per loop
$ python3 -m timeit '"baz" in ( "foo", "bar", "baz", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 )'
5000000 loops, best of 5: 53.8 nsec per loop

Even for very small collections:

$ python3 -m timeit '"bar" in { "foo", "bar" }'
20000000 loops, best of 5: 18.6 nsec per loop
$ python3 -m timeit '"bar" in [ "foo", "bar" ]'
10000000 loops, best of 5: 35.6 nsec per loop
$ python3 -m timeit '"bar" in ( "foo", "bar" )'
10000000 loops, best of 5: 36.1 nsec per loop

... pretty much only being slower - and even then, only marginally slower - when literally the first element in the collection is a hit:

$ python3 -m timeit '"foo" in { "foo", "bar" }'
20000000 loops, best of 5: 17.8 nsec per loop
$ python3 -m timeit '"foo" in [ "foo", "bar" ]'
20000000 loops, best of 5: 13.9 nsec per loop
$ python3 -m timeit '"foo" in ( "foo", "bar" )'
20000000 loops, best of 5: 14.8 nsec per loop

While I have not analyzed it in detail, I strongly suspect that at least some of these checks are in a hot path, where they could provide a significant performance improvement - and I expect that blindly changing all non-iterated list/tuple literals to set literals across the codebase, could provide a significant performance improvement with very little work.

It appears that the performance benefit comes from list/tuple literals with constant values being compiled to tuples, whereas set literals with constant values are compiled to frozensets, paying the entire set-building cost at compile time while incurring no runtime overhead.

Steps to reproduce

N/A

Version information

Current develop HEAD.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-PerformancePerformance, both client-facing and admin-facingz-p2(Deprecated Label)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0