Skip to content

Port C++ psis code to loo? #260

Open

Description

@SteveBronder

In Stan we have a lot of the code for psis sampling available as C++.

https://github.com/stan-dev/stan/blob/develop/src/stan/services/pathfinder/psis.hpp

It should be a good bit faster. How much time do you ballpark the psis sampling takes / would it be a lot better if it was faster in R?

We could do this by writing some Rcpp to call the functions in Stan. However this would be a large dependency to add so if the current speed is fine then idt we should worry about this too much.

But if speed is an issue happy to put this on my plate to do at some point

Activity

  1. jgabry commented on Jul 3, 2024

    @jgabry
    Member

    Thanks @SteveBronder, just saw this (finally catching up on GitHub issues!). Yeah I think the only reason not to do this would be adding the dependency on Rcpp. I guess, like you said, it all depends on whether the added speed would be a game changer. PSIS smoothing is definitely the biggest bottleneck when running loo(), but I've never tried comparing the R and C++ versions to see how much of a speedup there would be. @avehtari what do you think?

  2. avehtari commented on Oct 21, 2026

    @avehtari
    Member

    @SteveBronder @jgabry

    I measured this. I compiled Stan's psis.hpp from develop without changes, with a small shim for the three Stan Math functions it uses, and called psis_weights() per column through Rcpp. Its k values and weights agree with loo::psis() to about 1e-12 (with tied draws, the two assign the smoothed values to the tied draws in a different order).

    The C++ core is 3x to 4x faster than the R code for the same work, for example 0.12 s instead of 0.31 s for 4000 draws and 1000 observations on one laptop core. psis() also normalizes the weights and computes n_eff after the smoothing, so with a C++ core psis() itself would be about 1.6x to 2x faster: 0.33 s instead of 0.52 s in that example. loo() often also computes relative_eff(), which takes longer than psis() (0.65 s in the same example), and then the whole call would be about 1.2x faster.

    So just using psis.hpp, the gain is not big enough to justify a dependency on Rcpp and a compiled package.

    This inspired #413 to change do_psis_i() in R to sort only the tail instead of the whole column, with identical results, and psis() is then 1.1x to 1.4x faster than now, making the benefit of C++ even smaller.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions