You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Table.read(filename) is 10 times slower than Table(fits.getdata(filename)) #7399
As @astrofrog said, the file has mixin columns declaration in the FITS header (COMMENT --BEGIN-ASTROPY-SERIALIZED-COLUMNS--). Hence, fits.getdata will definitely be faster.
If mix-in columns are being used it may just be a matter of making sure we set copy=False when instantiating those objects? (since there's no reason a copy should be made). I don't have time to investigate this week, but this seems like a good #pyastro18 hack? :)
I haven't gone through the mixin specification code in much detail, but it is definitely worth considering how Table.read could be made easier in terms of user perspective. P. S. My bad, but I am definitely getting a little bit confused 😕 having both mixin yaml by default and astropy_native (although it is just for time) as an option. (correct me if I'm wrong)
I don't believe there are no bona-fide "mixin columns" in this file. All the columns are just standard Column types with the meta-data providing all the datatypes, formats, descriptions, and column meta (ucd's). A key thing to note is at the end of the COMMENTs:
The fact that __serialized_columns__ is an empty dict means there are no mixin-columns (i.e. a SkyCoord or Time or Quantity). Below is what mixins look like in a FITS file, so I'm a bit confused about why concatenate is being called and burning all the CPU.
In [7]: t = QTable([[1, 2] * u.m, SkyCoord([1,2], [3,4], unit='deg'), Time([1,2], format='cxcsec')])
In [8]: t.write('test.fits', overwrite=True)
In [9]: hdus = fits.open('test.fits')
In [10]: hdus[1].header
Out[10]:
XTENSION= 'BINTABLE' / binary table extension
BITPIX = 8 / array data type
NAXIS = 2 / number of array dimensions
NAXIS1 = 40 / length of dimension 1
NAXIS2 = 2 / length of dimension 2
PCOUNT = 0 / number of group parameters
GCOUNT = 1 / number of groups
TFIELDS = 4 / number of table fields
TIMESYS = 'UTC ' / Default time scale
JDREF = 0.0 / Time columns are jd = jd1 + jd2
TREFPOS = 'TOPOCENTER' / Time reference position
TTYPE1 = 'col0 '
TFORM1 = 'D '
TUNIT1 = 'm '
TTYPE2 = 'col1.ra '
TFORM2 = 'D '
TUNIT2 = 'deg '
TTYPE3 = 'col1.dec'
TFORM3 = 'D '
TUNIT3 = 'deg '
TTYPE4 = 'col2 '
TFORM4 = '2D '
TUNIT4 = 'd '
TCTYP4 = 'TT '
TCUNI4 = 'd '
TRPOS4 = ''
COMMENT --BEGIN-ASTROPY-SERIALIZED-COLUMNS--
COMMENT datatype:
COMMENT - {name: col0, unit: m, datatype: float64}
COMMENT - {name: col1.ra, unit: deg, datatype: float64}
COMMENT - {name: col1.dec, unit: deg, datatype: float64}
COMMENT - {name: col2, datatype: object}
COMMENT meta:
COMMENT __serialized_columns__:
COMMENT col0:
COMMENT __class__: astropy.units.quantity.Quantity
COMMENT value: !astropy.table.SerializedColumn {name: col0}
COMMENT col1:
COMMENT __class__: astropy.coordinates.sky_coordinate.SkyCoord
COMMENT dec: !astropy.table.SerializedColumn
COMMENT __class__: astropy.coordinates.angles.Latitude
COMMENT value: !astropy.table.SerializedColumn {name: col1.dec}
COMMENT frame: icrs
COMMENT ra: !astropy.table.SerializedColumn
COMMENT __class__: astropy.coordinates.angles.Longitude
COMMENT value: !astropy.table.SerializedColumn {name: col1.ra}
COMMENT wrap_angle: !astropy.coordinates.Angle
COMMENT unit: !astropy.units.Unit {unit: deg}
COMMENT value: 360.0
COMMENT representation_type: spherical
COMMENT --END-ASTROPY-SERIALIZED-COLUMNS--
When using Table.read(file) it is using the connnect.py code which pays attention to the NULL values.
This particular file actually has specified NULL values for a number of columns.
This causes read_table_fits to use a masked table and thus MaskedColumn for all the columns (so it can properly mask column entries that correspond to the FITS NULL value for that column).
BUT, for some reason I have not yet looked at, MaskedColumn is fantastically slow to initialize:
In [8]: d = np.arange(1e7)
In [9]: time x = Column(d)
CPU times: user 304 µs, sys: 8.24 ms, total: 8.54 ms
Wall time: 8.74 ms
In [10]: time x = MaskedColumn(d)
CPU times: user 2.08 s, sys: 43.1 ms, total: 2.12 s
Wall time: 2.13 s
I cannot blame masked array here:
In [12]: time x = np.ma.MaskedArray(d)
CPU times: user 74 µs, sys: 1 µs, total: 75 µs
Wall time: 79.9 µs
Activity
saimn commentedon Apr 23, 2018
The difference may be due to memmap, can you try with
Table.read(..., memmap=True)?adrn commentedon Apr 23, 2018
Doesn't look like it:


and
pllim commentedon Apr 23, 2018
@adrn , try to profile the slow case and see where the bottleneck is.
pllim commentedon Apr 23, 2018
If you give it
format='fits', does it load faster?pllim commentedon Apr 23, 2018
Also, to rule out overhead of first-time imports, time the faster one first before the slower one and see.
adrn commentedon Apr 23, 2018
First 16 lines of profiling:
adrn commentedon Apr 23, 2018
The lines in #7399 (comment) were run after the cells in #7399 (comment) so I think that already rules out first-time imports.
pllim commentedon Apr 23, 2018
This jumped out
adrn commentedon Apr 23, 2018
Yea, exactly - that concatenate looks culpable.
Nope.
pllim commentedon Apr 23, 2018
What does the faster case
prunlook like?adrn commentedon Apr 23, 2018
Faster case:
adrn commentedon Apr 23, 2018
And the "slow case" with
memmap=True:17 remaining items
saimn commentedon Apr 25, 2018
Hmm, Time conversion should only happen if
astropy_native=Truewhich is not the case by default... so maybe something else.astrofrog commentedon Apr 26, 2018
The following lines suggest to me that there may be mix-in columns declared as YAML in the FITS header:
@adrn - did you manage to upload the file somewhere?
manodeep commentedon May 1, 2018
Tagging myself because I might be of help. Would be useful to have a self-contained test case, with data
bsipocz commentedon May 1, 2018
@adrn - Could you upload your file to somewhere?
adrn commentedon May 2, 2018
File is here, sorry!
https://www.astro.princeton.edu/~adrn/Gaia-DR2-mock-200pc.fits
aaryapatil commentedon May 2, 2018
As @astrofrog said, the file has mixin columns declaration in the FITS header (COMMENT --BEGIN-ASTROPY-SERIALIZED-COLUMNS--). Hence, fits.getdata will definitely be faster.
aaryapatil commentedon May 2, 2018
On another note, there are definitely places where Table.read() can be made faster when
astropy_native=True. But that can be handled in a separate PR.astrofrog commentedon May 2, 2018
If mix-in columns are being used it may just be a matter of making sure we set
copy=Falsewhen instantiating those objects? (since there's no reason a copy should be made). I don't have time to investigate this week, but this seems like a good #pyastro18 hack? :)aaryapatil commentedon May 2, 2018
I haven't gone through the mixin specification code in much detail, but it is definitely worth considering how
Table.readcould be made easier in terms of user perspective. P. S. My bad, but I am definitely getting a little bit confused 😕 having both mixin yaml by default and astropy_native (although it is just for time) as an option. (correct me if I'm wrong)taldcroft commentedon May 2, 2018
I don't believe there are no bona-fide "mixin columns" in this file. All the columns are just standard
Columntypes with the meta-data providing all the datatypes, formats, descriptions, and column meta (ucd's). A key thing to note is at the end of the COMMENTs:The fact that
__serialized_columns__is an empty dict means there are no mixin-columns (i.e. a SkyCoord or Time or Quantity). Below is what mixins look like in a FITS file, so I'm a bit confused about whyconcatenateis being called and burning all the CPU.taldcroft commentedon May 2, 2018
FYI
taldcroft commentedon May 2, 2018
OK, well I at least have a top-level cause:
Table.read(file)it is using theconnnect.pycode which pays attention to the NULL values.read_table_fitsto use a masked table and thusMaskedColumnfor all the columns (so it can properly mask column entries that correspond to the FITS NULL value for that column).BUT, for some reason I have not yet looked at,
MaskedColumnis fantastically slow to initialize:I cannot blame masked array here:
manodeep commentedon May 2, 2018
Given the dis-proportionate time spent in sys compared to user, I am going to guess (multiple?) malloc's are occurring.
taldcroft commentedon May 3, 2018
See #7422 for a fix.