Recently we published Asymptotic Benchmarking Using the atime package, a paper about asymptotic performance analysis. For a new research paper on this topic, I am working on a framework which uses git bisect to identify the commit responsible for a performance regression.

Example 1: recent slowdown in data.table::fread

Three years ago, I was PI of the NSF POSE project about R data.table, a high-performance in-memory database system. Recently I re-ran some benchmarks, using updated software and hardware.

Below is the original from 2023, old code and old hardware:

read old code old laptop

Below is the new code and old hardware:

read new code old laptop

Below is the new code and new hardware:

read new code new laptop

We can see that the reading function is slower! When was this slowdown introduced?

I wrote some new functions in atime for answering this question.

I added a new test case in PR7907.

"fread N=cols regression" = atime::atime_test(
    N = 2^seq(0, 20), # smaller N because disk can be slow.
    setup={
      set.seed(1)
      n.rows <- 100
      input.vec <- rnorm(n.rows*N)
      input.mat <- matrix(input.vec, n.rows, N)
      input.dt <- data.table(input.mat)
      input.csv <- tempfile()
      data.table::fwrite(input.dt, input.csv)
    },
    seconds.limit=0.1,
    Fast="1.14.8",
    # Fast="1685a3b47d48f323afeae589545f7cbb7717ec28", #not as fast as Fast, but using this as Fast in bisect with Slow=1.18.6 yields PR#7370.
    # Slow="67db7f7fb33b99da8cc5dc575714924b96d0f0fe", #not as slow as Slow, but using this as Slow in bisect with Fast=1.14.8 yields PR#7375.
    Slow="1.18.6",
    expr=data.table::fread(input.csv, showProgress = FALSE, nThread=1)),

The key parts of the code below which enable performance bisecting are the Fast/Slow commits. We run git bisect with old=Fast and new=Slow, and we get this interesting result:

fread

We see in the first bisect on the left, that the bisect stopped with an intermediate performance, about halfway between Fast and Slow. So we re-ran git bisect twice more, and we found two different PRs related to fread, which likely caused the slowdown, see below.

#Fast="1685a3b47d48f323afeae589545f7cbb7717ec28", #not as fast as Fast, but use as Fast in bisect with Slow.
#Slow="1.18.6",
df7fa8071c6b818d24e47cc1a84a6dc0491550d6 is the first bad commit
commit df7fa8071c6b818d24e47cc1a84a6dc0491550d6
Author: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com>
Date:   Mon Nov 3 22:03:18 2025 +0100

    fix sep detection for single column quoted file (#7370)
    
    * fix sep detection for single column quoted file
    
    * restore old comment
    
    * remove unnecessary check
    
    * simplify quote scan
    
    * Renumber NEWS

 NEWS.md               |  2 ++
 inst/tests/tests.Rraw |  3 +++
 src/fread.c           | 27 +++++++++++++++++++++------
 3 files changed, 26 insertions(+), 6 deletions(-)
bisect found first bad commit

TODO more discussion.

#Fast="1.14.8",
#Slow="67db7f7fb33b99da8cc5dc575714924b96d0f0fe", #not as slow as Slow, but use as Slow in bisect with Fast.
59f966cf6839a0494e42ed1ab98616eea03dcb36 is the first bad commit
commit 59f966cf6839a0494e42ed1ab98616eea03dcb36
Author: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com>
Date:   Mon Oct 20 19:20:52 2025 +0200

    implement comment.char argument for fread (#7375)
    
    * implement comment.char argument for fread
    
    * remove handling of comment.char=NULL
    
    * update NEWS
    
    * update tests
    
    * change wording for error
    
    * remove unreachable code
    
    * add helper function
    
    * extend tests
    
    * update tests
    
    * use skip_line helper
    
    * add comments for helpers
    
    * fix test numbering
    
    * add more tests
    
    * simplify read
    
    * Revert "simplify read"
    
    This reverts commit a0a9525cd4b862d08d4eba9942086a2fbc2a101b.
    
    * separate helpers
    
    * add comments
    
    * add coverage
    
    * simplify header handling
    
    * increase coverage
    
    * simplify code
    
    * control skipping white spaces before comments with strip.white
    
    * tighten helper
    
    * try improving readability with blank lines
    
    * include some line-end comments in the multi-line comment test
    
    * match read.table for na.strings and comment.char
    
    * add strip.white=FALSE header testcase
    
    * refactor end_of_field helper into more readable version
    
    * add example for strip.white
    
    * summarize line-skipping behavior
    
    * clean up tmp
    
    * don't introduce whitespace to string literal body
    
    ---------
    
    Co-authored-by: Michael Chirico <chiricom@google.com>

 NEWS.md               |   1 +
 R/fread.R             |   7 ++-
 inst/tests/tests.Rraw | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++
 man/fread.Rd          |   3 +-
 src/data.table.h      |   2 +-
 src/fread.c           | 125 ++++++++++++++++++++++++++++++++++++++++++++-----
 src/fread.h           |   4 ++
 src/freadR.c          |   3 ++
 8 files changed, 257 insertions(+), 15 deletions(-)
bisect found first bad commit

Example 2

TODO discuss how atime gets the same results as in PR7912.

reg before

slow fast

Comparison between machines

Two computers (2010 and 2025), 10x absolute throughput differences, same qualitative results, same wall time.

reg before

slow fast

slow timeline

slow minutes

Example 3

poncatime TODO