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

py/objtype: Define all special methods if requested. #3381

Closed
wants to merge 3 commits into from

Conversation

pfalcon
Copy link
Contributor

@pfalcon pfalcon commented Oct 21, 2017

If MICROPY_PY_ALL_SPECIAL_METHODS is defined, actually define all special
methods (still subject to gating by e.g. MICROPY_PY_REVERSE_SPECIAL_METHODS).

This adds quite a number of qstr's, so should be used sparingly.

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 21, 2017

Final step for #3291. Perhaps, we should have MICROPY_PY_INPLACE_SPECIAL_METHODS for consistency.

I'm also open to suggestions that unix port should not now define MICROPY_PY_ALL_SPECIAL_METHODS (and what it should do instead). (Code size stats to follow.)

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 21, 2017

Master with this patch (unix x86_64):

 393012	   5920	   2464	 401396	  61ff4	micropython

Without patch, with special method mapping tables reverted to be of qstr[] type and special sorting in makeqstrdata.py removed:

 392940	   5920	   2464	 401324	  61fac	micropython

So, new MICROPY_PY_ALL_SPECIAL_METHODS gives +72 bytes overhead comparing to old non-optimized MICROPY_PY_ALL_SPECIAL_METHODS.

IMHO, quite acceptable. And I guess, we should define MICROPY_PY_INPLACE_SPECIAL_METHODS, and have them off by default, and instead enable reverse ops for Unix port, as they bring more functionality than inplace ones.

@dpgeorge
Copy link
Member

For more insight into code size changes: the changes which converted qstr to byte in the tables and sorted the special qstrs to the top reduced code size by:

   bare-arm:  -144
minimal x86:  -144
   unix x64:  -312
unix nanbox:  -336
      stm32:  -268
    esp8266:  -108
     cc3200:  -160

The changes in this PR introduce an increase of:

   bare-arm:    +0
minimal x86:    +0
   unix x64:  +712
unix nanbox:  +424
      stm32:  +328
    esp8266:  +340
     cc3200:  +296

So the NET change this PR brings compared to before any optimisations were made on the tables (which is the above numbers combined) is:

   bare-arm:  -144
minimal x86:  -144
   unix x64:  +400
unix nanbox:   +88
      stm32:   +60
    esp8266:  +232
     cc3200:  +136

Note that I'm using arm-none-eabi-gcc version 7.2.0, and gcc version 7.2.0 on Arch Linux which has PIC/PIE enabled by default, so the x64 binaries are much larger.

@dpgeorge
Copy link
Member

An alternative is to generate the inplace and reverse qstrs on the fly, only when needed (by inserting i/r in the normal op names). But that may lead to heap allocation during an operation which is no good.

Paul Sokolovsky added 3 commits October 27, 2017 20:06
If MICROPY_PY_ALL_SPECIAL_METHODS is defined, actually define all special
methods (still subject to gating by e.g. MICROPY_PY_REVERSE_SPECIAL_METHODS).

This adds quite a number of qstr's, so should be used sparingly.
This allows to configure support for inplace special methods separately,
similar to "normal" and reverse special methods. This is useful, because
inplace methods are "the most optional" ones, for example, if inplace
methods aren't defined, the operation will be executed using normal
methods instead.

As a caveat, __iadd__ and __isub__ are implemented even if
MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS isn't defined. This is similar
to the state of affairs before binary operations refactor, and allows
to run existing tests even if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS
isn't defined.
With inplace methods now disabled by default, it makes sense to enable
reverse methods, as they allow for more useful features, e.g. allow
for datetime module to implement both 2 * HOUR and HOUR * 2 (where
HOUR is e.g. timedelta object).
@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 27, 2017

Ok, added MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS setting, disabled by default (see commit message for caveats). Then, MICROPY_PY_REVERSE_SPECIAL_METHODS gets enabled for unix, which allows to use datetime objects more comfortably (and passes its 150K testsuite with only 8K patches (10% of testcases failing-made-skipped).

Going to merge this on weekend.

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 28, 2017

It's codesize check what failed this time. (To clarify, with th latest change in #3381 (comment), unix port should stay sabout the same (inplace ops off, reverse on instead), while the rest of port should have size decreased beyond @dpgeorge's figures in #3381 (comment) , because those include inplace ops, which are now off by default (reverse had been off already)).

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 28, 2017

Merged.

@pfalcon pfalcon closed this Oct 28, 2017
@dpgeorge
Copy link
Member

It's codesize check what failed this time.

Because __iadd__ and __isub__ are now included no matter what, there's no way to disable them, so bare-arm increased by +28 bytes. Was this the intention?

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 30, 2017

Because iadd and isub are now included no matter what, there's no way to disable them, so bare-arm increased by +28 bytes. Was this the intention?

The intention was to make options selecting special methods orthogonal. Apparently, before those were under ALL_SPECIAL_METHODS, but that makes less sense now that we have INPLACE_METHODS and REVERSE_METHODS, but keep having tests which assume __iadd__ and __isub__ is there.

We can put them back as they were, or can modify tests to use only __iadd__ and put that back. Or can switch that to __ixor__ completely (I have an idea to implement __ixor__ for bytearrays - even CPython doesn't have that ;^).

@pfalcon pfalcon reopened this Oct 30, 2017
@dpgeorge
Copy link
Member

The intention was to make options selecting special methods orthogonal.

Well, having 2**3 = 8 different options seems overkill in this situation. Likely there're only 3 configurations that are useful: minimal (all off), maximal (all on), some sensible middle ground which has the main useful ones enabled.

I suggest to either 1) make it like configuring error messages and provide 3 options as described above; or 2) make the options a bit less confusing by renaming MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS to MICROPY_PY_INPLACE_SPECIAL_METHODS and make it behave like the REVERSE option. Also I think __mul__ should be in the basic set of methods with __add__ and __sub__. So something like this:

    #if MICROPY_PY_INPLACE_SPECIAL_METHODS
    [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__,
    [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__,
    [MP_BINARY_OP_INPLACE_MULTIPLY] = MP_QSTR___imul__,
    #if MICROPY_PY_ALL_SPECIAL_METHODS
    [MP_BINARY_OP_INPLACE_FLOOR_DIVIDE] = MP_QSTR___ifloordiv__,
    [MP_BINARY_OP_INPLACE_TRUE_DIVIDE] = MP_QSTR___itruediv__,
    [MP_BINARY_OP_INPLACE_MODULO] = MP_QSTR___imod__,
    [MP_BINARY_OP_INPLACE_POWER] = MP_QSTR___ipow__,
    [MP_BINARY_OP_INPLACE_OR] = MP_QSTR___ior__,
    [MP_BINARY_OP_INPLACE_XOR] = MP_QSTR___ixor__,
    [MP_BINARY_OP_INPLACE_AND] = MP_QSTR___iand__,
    [MP_BINARY_OP_INPLACE_LSHIFT] = MP_QSTR___ilshift__,
    [MP_BINARY_OP_INPLACE_RSHIFT] = MP_QSTR___irshift__,
    #endif
    #endif

    [MP_BINARY_OP_ADD] = MP_QSTR___add__,
    [MP_BINARY_OP_SUBTRACT] = MP_QSTR___sub__,
    [MP_BINARY_OP_MULTIPLY] = MP_QSTR___mul__,
    #if MICROPY_PY_ALL_SPECIAL_METHODS
    [MP_BINARY_OP_FLOOR_DIVIDE] = MP_QSTR___floordiv__,
    [MP_BINARY_OP_TRUE_DIVIDE] = MP_QSTR___truediv__,
    [MP_BINARY_OP_MODULO] = MP_QSTR___mod__,
    [MP_BINARY_OP_DIVMOD] = MP_QSTR___divmod__,
    [MP_BINARY_OP_POWER] = MP_QSTR___pow__,
    [MP_BINARY_OP_OR] = MP_QSTR___or__,
    [MP_BINARY_OP_XOR] = MP_QSTR___xor__,
    [MP_BINARY_OP_AND] = MP_QSTR___and__,
    [MP_BINARY_OP_LSHIFT] = MP_QSTR___lshift__,
    [MP_BINARY_OP_RSHIFT] = MP_QSTR___rshift__,
    #endif

    #if MICROPY_PY_REVERSE_SPECIAL_METHODS
    [MP_BINARY_OP_REVERSE_ADD] = MP_QSTR___radd__,
    [MP_BINARY_OP_REVERSE_SUBTRACT] = MP_QSTR___rsub__,
    [MP_BINARY_OP_REVERSE_MULTIPLY] = MP_QSTR___rmul__,
    #if MICROPY_PY_ALL_SPECIAL_METHODS
    [MP_BINARY_OP_REVERSE_FLOOR_DIVIDE] = MP_QSTR___rfloordiv__,
    [MP_BINARY_OP_REVERSE_TRUE_DIVIDE] = MP_QSTR___rtruediv__,
    [MP_BINARY_OP_REVERSE_MODULO] = MP_QSTR___rmod__,
    [MP_BINARY_OP_REVERSE_POWER] = MP_QSTR___rpow__,
    [MP_BINARY_OP_REVERSE_OR] = MP_QSTR___ror__,
    [MP_BINARY_OP_REVERSE_XOR] = MP_QSTR___rxor__,
    [MP_BINARY_OP_REVERSE_AND] = MP_QSTR___rand__,
    [MP_BINARY_OP_REVERSE_LSHIFT] = MP_QSTR___rlshift__,
    [MP_BINARY_OP_REVERSE_RSHIFT] = MP_QSTR___rrshift__,
    #endif
    #endif

@pfalcon
Copy link
Contributor Author

pfalcon commented Oct 31, 2017

Well, having 2**3 = 8 different options seems overkill in this situation. Likely there're only 3 configurations that are useful: minimal (all off), maximal (all on), some sensible middle ground which has the main useful ones enabled.

I follow a different line: it's better to let users decide what they want, and it's maintainers' responsibility to make sure that's possible and generic, deconfused as much as reasonably possible. (But it's also maintainers' responsibility to set the default applying to most users, and as there's no single way to satisfy everyone, it always will be a compromise - and maintainers' are at better position to do that (than e.g. complaining users)).

So, having 3 options, controlling 3 different groups of operators, seems pretty reasonable approach, and it even already gives its benefits - I was ready to enable all operators for unix port at all, but turned out, for the immediate milestone at hand (datatime module), only normal and reverse operators are required.

Regarding the rest, all different options options were of course considered, and some even described above, e.g. why it was named MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (to not break existing tests).

@pfalcon
Copy link
Contributor Author

pfalcon commented Nov 5, 2017

Ok, action items here:

  1. Do we want to exclude inplace methods tests for normal unix build in an adhoc way (and run only on coverage build?
  2. If no, do we want to patch them to test just a single inplace method (instead of both __iadd__ and __isub__), and maybe make that method be __ixor__? (The motivation follows this: extmod/moducryptolib: Add crypto functions module, impl reuses axTLS routines. #3413)

@dpgeorge
Copy link
Member

Since there was no progress here, and spending effort to make small changes here seems counter productive, I'll close this.

In 0a36a80 I updated a comment related to this PR.

@dpgeorge dpgeorge closed this Sep 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants