Search code examples
rperformancesum

How is R able to sum an integer sequence so fast?


Create a large contiguous sequence of integers:

x <- 1:1e20

How is R able to compute the sum so fast?

sum(x)

Doesn't it have to loop over 1e20 elements in the vector and sum each element?


Solution

  • Summing up the comments:

    R introduced something called ALTREP, or ALternate REPresentation for R objects. Its intent is to do some things more efficiently. From https://www.r-project.org/dsc/2017/slides/dsc2017.pdf, some examples include:

    • allow vector data to be in a memory-mapped file or distributed
    • allow compact representation of arithmetic sequences;
    • allow adding meta-data to objects;
    • allow computations/allocations to be deferred;
    • support alternative representations of environments.

    The second and fourth bullets seem appropriate here.

    We can see a hint of this in action by looking at what I'm inferring is at the core of the R sum primitive for altreps, at https://github.com/wch/r-source/blob/7c0449d81c853f781fb13e9c7118065aedaf2f7f/src/main/altclasses.c#L262:

    static SEXP compact_intseq_Sum(SEXP x, Rboolean narm)
    {
    #ifdef COMPACT_INTSEQ_MUTABLE
        /* If the vector has been expanded it may have been modified. */
        if (COMPACT_SEQ_EXPANDED(x) != R_NilValue) 
        return NULL;
    #endif
        double tmp;
        SEXP info = COMPACT_SEQ_INFO(x);
        R_xlen_t size = COMPACT_INTSEQ_INFO_LENGTH(info);
        R_xlen_t n1 = COMPACT_INTSEQ_INFO_FIRST(info);
        int inc = COMPACT_INTSEQ_INFO_INCR(info);
        tmp = (size / 2.0) * (n1 + n1 + inc * (size - 1));
        if(tmp > INT_MAX || tmp < R_INT_MIN)
        /**** check for overflow of exact integer range? */
        return ScalarReal(tmp);
        else
        return ScalarInteger((int) tmp);
    }
    

    Namely, the reduction of an integer sequence without gaps is trivial. It's when there are gaps or NAs that things become a bit more complicated.

    In action:

    vec <- 1:1e10
    sum(vec)
    # [1] 5e+19
    sum(vec[-10])
    # Error: cannot allocate vector of size 37.3 Gb
    ### win11, R-4.2.2
    

    Where ideally we would see that sum(vec) == (sum(vec[-10]) + 10), but we cannot since we can't use the optimization of sequence-summing.