Skip to content

Reuse a fit's CSV metadata on subsequent reads #1314

Open

Description

@jgabry

I noticed then when reviewing #1312 and I had Claude run some tests and help generate this report.


read_csv_metadata() greps every chain file for its comment lines, so the cost of reading the metadata grows with the number of draws even though the metadata is a few hundred lines. Every read_cmdstan_csv() call pays it per file: every $draws() on a variable not yet read, every $summary() and $sampler_diagnostics() on a fresh fit, and as_cmdstan_fit().

Measured on macOS with CmdStan 2.40.0, 4 chains, 1000 draws, medians of 5:

per chain file 4 metadata scans one-variable read_cmdstan_csv() call
10 parameters, 0.16 MB 0.12 s 0.15 s
1000 parameters, 11 MB 0.44 s 0.91 s

So the scan is most of a read on a small fit and about half on a large one, roughly 10 ms per MB of CSV on each read.

The fix is for a fit to reuse its own metadata: after the first read the fit holds the merged metadata and its files don't change, so later reads pass it back to the reader and skip the scan. That leaves one scan per fit, on the first read.

Possible design: the parse-and-merge loop in read_cmdstan_csv() becomes merge_csv_metadata(files), returning the merged metadata with the per-chain inverse metric and step size lists. The body of read_cmdstan_csv() moves to an internal function with a csv_metadata argument that skips the merge when given one and returns it, and the exported function stays a wrapper with the same arguments and return value. CmdStanMCMC and CmdStanGQ (the two classes that read more than once) store the merged metadata on the first read and pass it on later ones. The processed metadata the fit already stores can't be handed back directly: the reader selects columns by the raw CSV names (theta.1) and the fit's copy has the repaired ones (theta[1]).

Activity

  1. VisruthSK commented on Oct 7, 2026

    @VisruthSK
    Member

    A reader that takes the head until the first data...

    I thought a little about this when writing #1312, and the main issue I had with a custom scan was that we would need to roll our own reader, right? I'm not super familiar with grep so maybe it's possible to give it bounds which would be nice, but writing our own reader almost seems worse than eating the performance?

    But if it's much easier/safer than I'm thinking to write our own reader, or there's a nice way to shop it out to a dep I think that reading just the metadata would be very good to have.

  2. jgabry commented on Oct 7, 2026

    @jgabry
    MemberAuthor

    Good point, you're right! I still don't think we should add the cache, but I don't think we should do our own reader either.

    I think we can just stop re-parsing at all for a fit we already read. After the first read the fit has the merged metadata and its files won't change, so later reads ($draws() on a new variable, $summary(), $sampler_diagnostics()) can just pass the info back to read_cmdstan_csv() and skip the scan. Right? I think that should work, but I'll double check. Then we just have one scan per fit on the first read, which is fine.

  3. changed the title [-]Don't scan whole file when reading the metadata of a CSV file[/-] [+]Reuse a fit's CSV metadata on subsequent reads[/+] on Oct 7, 2026
  4. jgabry commented on Oct 7, 2026

    @jgabry
    MemberAuthor

    I updated the issue body with the new idea, which isn't quite as simple as just passing it back to read_cmdstan_csv(), but I think is still relatively simple.

  5. VisruthSK commented on Oct 7, 2026

    @VisruthSK
    Member

    Yeah the proposed solutions seems good.

    It might not be that bad to write a little reader, so maybe doing a little experiment is worthwhile to see the performance gains? Would really depend on how complex the reader would have to be and how much runtime or memory is saved.

  6. added
    internal-codeTests, code cleanup, refactoring, or other things not user facing
    on Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    internal-codeTests, code cleanup, refactoring, or other things not user facing

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions