Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache Bit and Register __repr__ and use for __eq__ #5272

Merged
merged 11 commits into from Nov 25, 2020

Conversation

mtreinish
Copy link
Member

@mtreinish mtreinish commented Oct 21, 2020

Summary

This commit caches the generated repr for the Bit class (and its
subtypes Qubit, Clbit, etc) and Register class at init time. Currently
when we create DAGNode objects in the dagcircuit a big portion of
the time is spent regenerating the str for the repr. However this
information is static and the string manipulation which is slow
doesn't need to be run constantly.

Details and comments

This commit caches the generated repr for the Bit object (and its
subtypes Qubit, Clbit, etc) at init time. Currently when we create
DAGNode objects in the dagcircuit a big portion of the time is spent
regenerating the str for the repr. However this information is static
and the string manipulation which is slow doesn't need to be run
constantly.
@mtreinish mtreinish requested a review from a team as a code owner October 21, 2020 15:59
@mtreinish mtreinish changed the title Cache Bit __repr__ Cache Bit and Register __repr__ Oct 21, 2020
@mtreinish
Copy link
Member Author

The main goal of this is to speed up DAGNode creation (mainly in the context of apply_operation_back). The improvement today is small compared to the overall speed of that method. Looking at circuit_to_dag (which is mainly apply_operation_back) for a random 65 qubit circuit with a depth of 4096, with master today the profile looks like:

Screenshot_2020-10-21_13-13-05

While with this PR:

Screenshot_2020-10-21_13-15-12

(in both profiles the pink block is Bit.__repr__)

Where this comes up more is after #5267 merges, because then the __repr__ is a more substantial portion of the time spent in apply_operation_back() so shrinking the time we spend in Bit.__repr__ has a larger impact.

This commit updates the __eq__ method of both Bit and Register to just
compare the cached _repr value. The previous __eq__ methods were nested
checks of various attributes, and while individually those are all fast
when doing large list comprehensions the multiple checks in each
function add up in runtime. All the information in the _repr attribute
are identical to the multiple checks previously done in __eq__, so this
reduces the __eq__ function to a single string comparison which in
aggregate ends up being significantly faster.
@mtreinish mtreinish changed the title Cache Bit and Register __repr__ Cache Bit and Register __repr__ and use for __eq__ Oct 21, 2020
@mtreinish
Copy link
Member Author

To continue the speed up after looking at profiles from #5267 (comment) I realized the next biggest time sync in apply_operation_back() was comparing bits with Bit.__eq__ and that the cached repr we added here has all the necessary information to do that in a single string comparison instead of multiple layers of nested checks. So in 061583b I modified eq to make this optimization and with that and #5267 applied it makes a noticeable impact on the speed of apply_operation_back(). See: #5267 (comment)

@mtreinish mtreinish added the stable backport potential The bug might be minimal and/or import enough to be port to stable label Oct 29, 2020
@kdk kdk self-assigned this Nov 4, 2020
@mtreinish
Copy link
Member Author

I just ran profiles of the same 65 by 4096 random circuit circuit to dag conversion again, and with __eq__ this makes a decent impact even without #5267. Without this PR the profile of apply_operation_back() looks like:

Screenshot_2020-11-04_16-38-22

Then with this PR in it's current form (caching __repr__ and using that for __eq__):

Screenshot_2020-11-04_16-37-37

@mtreinish mtreinish removed the stable backport potential The bug might be minimal and/or import enough to be port to stable label Nov 4, 2020
@mtreinish
Copy link
Member Author

I'm removing the stable backport tag here. I don't think the performance improvement here is really worth the risk of regression on the stable branch. It's more than I thought without #5267 but it doesn't really seem big enough to backport.

mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Nov 16, 2020
In sabre_swap the comprehension in _score_heuristic() with the basic
heuristic starts to become a bottleneck as the size of the coupling map
increases. This probably becomes the top bottleneck in a transpilation
after Qiskit#5316, Qiskit#5183, Qiskit#5294, Qiskit#5267, and Qiskit#5272 are merged. This commit is
an attempt to try and mitigate that a bit by using numpy native
operations instead of a python comprehension in sum(). If this is
insufficient we'll likely have to either avoid doing a sum like this or
drop down to a lower level with cython or rust and operate on the array
directly to remove this bottleneck.
@mergify mergify bot merged commit daafa7e into Qiskit:master Nov 25, 2020
mergify bot added a commit that referenced this pull request Nov 25, 2020
* Use numpy sum instead of comprehension in _score_heuristic

In sabre_swap the comprehension in _score_heuristic() with the basic
heuristic starts to become a bottleneck as the size of the coupling map
increases. This probably becomes the top bottleneck in a transpilation
after #5316, #5183, #5294, #5267, and #5272 are merged. This commit is
an attempt to try and mitigate that a bit by using numpy native
operations instead of a python comprehension in sum(). If this is
insufficient we'll likely have to either avoid doing a sum like this or
drop down to a lower level with cython or rust and operate on the array
directly to remove this bottleneck.

* Make distance matrix a coupling map property

* Fix tests

* Fix lint

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
@mtreinish mtreinish deleted the cache_repr_bits branch November 26, 2020 15:52
mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Nov 30, 2020
In Qiskit#5272 the __eq__ method was changed to use a single string comparison
away from a nested attribute comparison for performance reasons. This
had the unexpected side effect that when comparing a register or a bit
object against an object of another type an AttributeError would be
raised because the private _repr attribute doesn't exist outside of
those classes. This leads to unexpected failures when running with aqua
because they were comparing a register to a string. This commit
addresses this by catching the attribute error and returning false
because objects of different types are not equal.
mergify bot pushed a commit that referenced this pull request Nov 30, 2020
In #5272 the __eq__ method was changed to use a single string comparison
away from a nested attribute comparison for performance reasons. This
had the unexpected side effect that when comparing a register or a bit
object against an object of another type an AttributeError would be
raised because the private _repr attribute doesn't exist outside of
those classes. This leads to unexpected failures when running with aqua
because they were comparing a register to a string. This commit
addresses this by catching the attribute error and returning false
because objects of different types are not equal.
@kdk kdk added the Changelog: None Do not include in changelog label Mar 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants