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

Update expectations of subclasses #144

Closed
wants to merge 24 commits into from
Closed

Update expectations of subclasses #144

wants to merge 24 commits into from

Conversation

nbraud
Copy link
Collaborator

@nbraud nbraud commented Apr 6, 2019

  • Document status quo.
  • Propose support of subclasses that define new attributes, described in documentation and tests.
    Those are required to be dataclasses, to implement __new__, and not to implement __init__.
  • Implement that support using update()
    Typing guarantees are weakened: returned vectors are now guaranteed to have the type of self.
  • Make the references friendlier, in the inheritance docs.
  • Measure whether there is a performance impact.
  • Fix the performance regression.
  • Fix the issue with __new__ (see Vector2.__new__ violates its interface contract #146).

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 7, 2019

Lints are currently failing with mypy complaining about (the uses of) the constructor for LabeledVector, in tests/subclass.

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 7, 2019

Just to clarify, I made this so we could have a concrete proposal to discuss and iterate on.
I don't necessarily expect what gets eventually merged to be close to my initial proposal.

@nbraud nbraud mentioned this pull request Apr 7, 2019
@nbraud
Copy link
Collaborator Author

nbraud commented Apr 7, 2019

I “fixed” the mypy issue this morning (with type: ignore annotations...) but I wouldn't mind knowing how to properly do this :)

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 10, 2019

So, any comments about the suggested change?

Copy link
Collaborator

@pathunstrom pathunstrom left a comment

Choose a reason for hiding this comment

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

This all looks reasonable to me. I missed the addition of _unpack and will start a conversation elsewhere about that.

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 11, 2019

I finally took the time to run the benchmarks, and the results seem pretty clear: there's a large performance hit for all methods that return a vector:

$ python3 -m perf compare_to tests/benchmark_*.json --table                                                                             
+-----------+------------------+-------------------------------+
| Benchmark | benchmark_master | benchmark_subclass            |
+===========+==================+===============================+
| __add__   | 2.88 us          | 5.85 us: 2.03x slower (+103%) |
+-----------+------------------+-------------------------------+
| __sub__   | 2.92 us          | 5.88 us: 2.01x slower (+101%) |
+-----------+------------------+-------------------------------+
| reflect   | 9.46 us          | 16.2 us: 1.72x slower (+72%)  |
+-----------+------------------+-------------------------------+
| dot       | 618 ns           | 595 ns: 1.04x faster (-4%)    |
+-----------+------------------+-------------------------------+
| isclose   | 7.83 us          | 11.5 us: 1.47x slower (+47%)  |
+-----------+------------------+-------------------------------+
| __neg__   | 2.50 us          | 5.61 us: 2.24x slower (+124%) |
+-----------+------------------+-------------------------------+
| normalize | 6.46 us          | 13.3 us: 2.06x slower (+106%) |
+-----------+------------------+-------------------------------+
| rotate    | 4.06 us          | 7.69 us: 1.89x slower (+89%)  |
+-----------+------------------+-------------------------------+
| scale_by  | 2.23 us          | 5.39 us: 2.41x slower (+141%) |
+-----------+------------------+-------------------------------+
| scale_to  | 6.28 us          | 13.1 us: 2.09x slower (+109%) |
+-----------+------------------+-------------------------------+

Not significant (5): angle; __eq__; Vector2; length; truncate

Would there be a way to either:

  1. have a faster, specialized version of Vector2.update (that can only deal with modifying x and y),
  2. or make Vector2.update faster?

@AstraLuma
Copy link
Member

Vector2.update() is literally dataclasses.replace(). Looking at the source, it's a single loop over the fields.

Optimizing that sounds non-trivial, and possibly involve native code.

The fact that it is significantly slower than the class-searching stuff is disconcerting, though.

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 11, 2019

@astronouth7303 That was my point, though: dataclasses.update is a completely-generic thing, that doesn't know a priori whichs fields are in its arguments or in the dataclass, whereas we should be able to do much better (for instance with a shallow copy of the object, then setting x and y).

@nbraud nbraud added the blocked Blocked by another issue, potentially external to the project. label Apr 12, 2019
@nbraud
Copy link
Collaborator Author

nbraud commented Apr 12, 2019

Fixing the performance regression is blocked by #146.

@AstraLuma
Copy link
Member

AstraLuma commented Apr 12, 2019

Except that we don't know what fields there will be? That's the whole point of this whole thing?

@nbraud
Copy link
Collaborator Author

nbraud commented Apr 12, 2019

@astronouth7303 Yes, but we know that we only ever want to modify x and y, and that they exist (as in, are attributes of the object), so if we can do a quick shallow-copy and then update x and y, that's fine.

This was referenced Apr 14, 2019
Copy link
Member

@AstraLuma AstraLuma left a comment

Choose a reason for hiding this comment

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

I'd still prefer a .__copy__() that just returns self.

ppb_vector/vector2.py Outdated Show resolved Hide resolved
tests/test_subclass.py Outdated Show resolved Hide resolved
tests/test_subclass.py Outdated Show resolved Hide resolved
@nbraud
Copy link
Collaborator Author

nbraud commented Apr 16, 2019

I'd still prefer a .copy() that just returns self.

@astronouth7303 How is this related to (the current state of) this PR?
We can implement __copy__ later, assuming update (or some specialized version of it) doesn't do copy.copy and in-place mutation (but that would be unlikely, since copy.copy uses the pickling protocol, which is quite slow)

tests/test_subclass.py Outdated Show resolved Hide resolved
@nbraud nbraud mentioned this pull request May 9, 2019
5 tasks
bors bot added a commit that referenced this pull request May 22, 2019
149: Drop subclassing support r=astronouth7303 a=nbraud

- [x] Document that subclassing isn't supported.
- [x] Drop the subclassing testsuite.
- [x] Drop the subclassing support.
- [x] Rename `Vector2` to `Vector`.
  Avoid breaking backwards-compatibility by adding a `Vector2` alias.
- [x] Rename `vector2.py` to `vector.py`.

Closes #144 (this is an alternative proposal)

Co-authored-by: Nicolas Braud-Santoni <nicolas@braud-santoni.eu>
@bors bors bot closed this in #149 May 22, 2019
@nbraud nbraud deleted the subclass branch May 25, 2019 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked Blocked by another issue, potentially external to the project.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants