Skip to content

Time string formatting (isot/iso/fits) is slow — mostly Python overhead #19929

Closed

Description

@veyron-kairo

What is the problem this feature will solve?

Converting a large Time array to a string format is dominated by Python overhead rather than the actual calculation.

import numpy as np
from astropy.time import Time
tm = Time(2451545.0 + np.random.uniform(0, 3650, 100_000), format='jd')
%timeit tm.isot   # ~150 ms

Profiling .isot on 100k elements shows the ERFA call that does the real work (d2dtf) takes ~4 ms; the remaining ~145 ms is the per-element Python loop in TimeString.value that calls str.format once per time. The code already flags it, with # Try to optimize this later and # TODO: fix this ugly hack right at that spot.

This affects every TimeString format (iso, isot, yday, fits, datetime64, and the epoch strings), so it slows down printing and ECSV/FITS writing of any table with a Time column.

Describe the desired outcome

Vectorize the formatting. ERFA already returns vectorized year/month/day/h/m/s arrays, so the string assembly can be done with array operations instead of a Python loop.

A quick numpy prototype that writes the digits into a fixed-width byte buffer produces byte-identical output at roughly 12x faster (150 ms -> 13 ms on the example above). A small C formatter mirroring the existing fast parser in _parse_times.c (which already handles the reverse string -> jd direction) could push closer to the ~4 ms ERFA floor.

Additional context

Relevant code: TimeString.value, str_kwargs, and format_string in astropy/time/formats.py. I'm happy to work on this.

Activity

  1. veyron-kairo commented on Jun 16, 2026

    @veyron-kairo
    ContributorAuthor

    @taldcroft @mhvk, as maintainers of the time code, your input would be appreciated here. The main design question is whether a pure-numpy vectorization is sufficient, or whether a C formatter mirroring the existing fast parser is warranted. I am happy to open a PR for whichever approach you prefer.

  2. mhvk commented on Jun 17, 2026

    @mhvk
    Contributor

    @veyron-kairo - thanks for the ping! I think replacing the python loop is a good idea. My sense is that a numpy replacement would be good enough, especially since you seem to have a prototype already. Also, this is not likely to be in the critical path (one writes files must less frequently than one reads them), and C code is less maintainable.

  3. taldcroft commented on Jun 17, 2026

    @taldcroft
    Member

    @veyron-kairo - great idea, improving performance by an order of magnitude or more would be fantastic!

  4. veyron-kairo commented on Jun 17, 2026

    @veyron-kairo
    ContributorAuthor

    Thanks so much @mhvk @taldcroft, really glad you're both on board! 🙌

    That settles it for me — I'll go with the pure-numpy approach. Totally agree on keeping it out of C: it's not on the critical read path, and the maintainability win is worth far more than squeezing the last few ms toward the ERFA floor.

    I'll polish up the prototype into a proper PR — writing the digits straight into a fixed-width byte buffer so the output stays byte-identical across all the TimeString formats (iso, isot, yday, fits, datetime64, and the epoch strings), with tests to lock that down. Will ping you here once it's up. Excited to get this one in!

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