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

Improve performance of multivector arithmetic by avoiding copies #280

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

eric-wieser
Copy link
Member

@eric-wieser eric-wieser commented Mar 12, 2020

Multiplications become roughly 20% faster, and addition 33% faster.
There's now a new copy argument to the MultiVector constructor.

Some timings:

Edit: These timings are out of date, and include the effects of #283 which used to be part of this PR. We should retime before merging

After:

In [1]: from clifford import Cl
In [2]: layout, _ = Cl(5)
In [3]: a = layout.randomMV(); b = layout.randomMV()

In [4]: %timeit a * b
The slowest run took 4.76 times longer than the fastest. This could mean that an intermediate result is being cached.
20.4 µs ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit a * b
8.64 µs ± 341 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [6]: %timeit a * b
9.23 µs ± 606 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit a + b
3.9 µs ± 250 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Before:

In [1]: from clifford import Cl
In [2]: layout, _ = Cl(5)
In [3]: a = layout.randomMV(); b = layout.randomMV()

In [4]: %timeit a * b
The slowest run took 4.37 times longer than the fastest. This could mean that an intermediate result is being cached.
18.6 µs ± 14.2 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit a * b
11.7 µs ± 759 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [6]: %timeit a * b
11.3 µs ± 660 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit a + b
6.11 µs ± 871 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

You'll be able to see Jupyter notebook diff and discuss changes. Powered by ReviewNB.

@eric-wieser
Copy link
Member Author

I'm wondering if actually we should just change the default to copy=False. I haven't yet found an internal call that would break if we did this.

@lgtm-com
Copy link

lgtm-com bot commented Mar 12, 2020

This pull request introduces 1 alert and fixes 6 when merging 31ff317 into a5c9431 - view on LGTM.com

new alerts:

  • 1 for Syntax error

fixed alerts:

  • 3 for Variable defined multiple times
  • 2 for Unused import
  • 1 for 'import *' may pollute namespace

The behavior of `MultiVector(layout, some_array)` has always been to copy `some_array`.

This change makes that copy opt-out, via `MultiVector(layout, some_array, copy=False)`.
This is used internally by all of the arithmetic operators to avoid unnecessary copies.
@eric-wieser eric-wieser changed the title Improve performance of multivector arithmetic Improve performance of multivector arithmetic by avoiding copies Mar 12, 2020
@@ -195,8 +195,8 @@ def ga_exp(B):
Fast implementation of the translation and rotation specific exp function
"""
if np.sum(np.abs(B.value)) < np.finfo(float).eps:
return layout.MultiVector(unit_scalar_mv.value)
return layout.MultiVector(val_exp(B.value))
return layout.MultiVector(unit_scalar_mv.value, copy=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause problems as the unit_scalar is a constant scoped to the whole file

Copy link
Member Author

@eric-wieser eric-wieser Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed - however, I don't think this is any worse than the fact that clifford.g3c.e3 is global, and I could do something careless like

my_vector = e3
my_vector.value[:] = e2.value  # whoops, now e3 == e2

Perhaps I'll put my patch in to fix that first - the blades that are part of the predefined algebras should definitely be readonly. I'm not sure if layout.blades should be though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omg I hate it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I have no strong feeling about layout.blades but the predefined blades definitely have to be readonly

Copy link
Member Author

@eric-wieser eric-wieser Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eo.value[:] = layout.scalar.value[:]
print("Your CGA is now somewhat PGA")

@eric-wieser
Copy link
Member Author

Let's not merge this till we:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants