Skip to content

Accessing Column.quantity always performs expensive deepcopy operation #14244

Open

Description

@maxnoe

Description

Accessing astropy.table.Column.quantity always performs an expensive deepcopy of the .info object, although this information can't even be accessed on temporary objects:

In [19]: t['a'].quantity.info
...
AttributeError: failed to access "info" attribute on a temporary object.

So something that is expensive is done also in cases where it can never actually be usefull, e.g. in situations like:

c = t['a'].quantity + b

Expected behavior

Perform expensive operations only when actually needed.

Actual behavior

DataInfo.__set__ is called which performs expensive, not needed operations.

Steps to Reproduce

In [1]: import astropy.units as u
In [2]: from astropy.table import Table
In [3]: t = Table({"a": [1, 2, 3] * u.deg})
In [4]: b = [1, 2, 3] * u.deg

In [5]: %prun -s tottime t['a'].quantity + b
         170 function calls (166 primitive calls) in 0.000 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 data_info.py:373(__set__)
        8    0.000    0.000    0.000    0.000 copy.py:128(deepcopy)

With timings:

In [9]: %%prun -s tottime
   ...: for i in range(100000):
   ...:     t['a'].quantity + b
   ...: 
         16700003 function calls (16300003 primitive calls) in 11.563 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   800000    1.390    0.000    2.634    0.000 copy.py:128(deepcopy)
   200000    1.182    0.000    5.392    0.000 data_info.py:373(__set__)
   300000    0.674    0.000    6.732    0.000 quantity.py:584(__array_finalize__)
   100000    0.554    0.000    4.729    0.000 quantity.py:434(__new__)
   100000    0.548    0.000    5.821    0.000 quantity.py:620(__array_ufunc__)

System Details

Linux-5.14.0-1051-oem-x86_64-with-glibc2.35
Python 3.9.15 | packaged by conda-forge | (main, Nov 22 2022, 08:45:29)
[GCC 10.4.0]
Numpy 1.23.5
pyerfa 2.0.0.1
astropy 5.2
Scipy 1.9.3
Matplotlib 3.6.2

Activity

  1. mhvk commented on Jan 2, 2023

    @mhvk
    Contributor

    It is not really a bug, and certainly the equivalent is used for changing columns from aTable to quantities in a QTable. But I can see it is annoying. In principle, I guess one could change the implementation to Quantity(col.data, unit=col.unit, dtype=col.dtype, copy=False, order="A", subok=True) -- it is unlikely there are many cases where people rely on .info being set when they do col.quantity.

    I wonder, though, why this comes up, as the presence of .quantity was really most to work-around the fact that Quantity could originally not be part of a Table. QTable does not have this restriction; is there a reason not to use that instead?

  2. maxnoe commented on Jan 3, 2023

    @maxnoe
    MemberAuthor

    We had some issues related to metadata access when using pure tables, not QTable, let me dig that up. If using QTable solves that, we should probably just switch to using QTable.

  3. maxnoe commented on Jan 3, 2023

    @maxnoe
    MemberAuthor

    Using a qtable is still somehow slower than using the pure table column, although the units are handled correctly and a quantity is returned by the pure column operation.

    In [4]: %timeit qt['a'] + b
    15.4 µs ± 475 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
    
    In [5]: %timeit t['a'] + b
    5.8 µs ± 31.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
    
  4. mhvk commented on Jan 3, 2023

    @mhvk
    Contributor

    But in a pure table, you presumably have an actual Column, so the unit is ignored, which would naturally make it faster.

  5. maxnoe commented on Jan 3, 2023

    @maxnoe
    MemberAuthor

    No, the unit is not ignored, it also works with the column in deg and the other quantity in rad, giving the correct result.

  6. mhvk commented on Jan 3, 2023

    @mhvk
    Contributor

    Ah, I see, so b is a Quantity. I checked and the difference is actually related to this issue: qt['a'] + b copies info and stores it on the result. Which seems a bug: after the addition, the name, etc., really shouldn't be kept! See #14248 and actually also the much older #5047.

  7. mhvk commented on Jan 4, 2023

    @mhvk
    Contributor

    So at least the slowdown you found in #14244 (comment) should be addressed by #14253

  8. mhvk commented on Jan 5, 2023

    @mhvk
    Contributor

    #14253 is merged. Hopefully this removes one disadvantage of QTable!

  9. mhvk commented on Jan 6, 2023

    @mhvk
    Contributor

    Coming back to the topic of this PR, I think that in doing col.quantity a user really would not generally expect that the name and description of a column would be preserved, so that it would be fine to change the implementation to be the faster Quantity(col.data, unit=col.unit, dtype=col.dtype, copy=False, order="A", subok=True) I suggested above. My only question really is whether it is worth it... Let me ping @taldcroft for suggestions.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions