The two-millisecond function called forty thousand times

Nothing in the profile looked slow, and one function accounted for 80 seconds of a 94-second job.

the profile, sorted by total time:

  self    calls    fn
  80.2s   41,208   Money::fromCents
   4.1s        1   Report::render
   2.8s      412   Repository::find

Money::fromCents was 1.9ms, and was validating the
currency code against a database-backed list.

caching the list: 80.2s → 0.4s

Sorting a profile by self time per call finds the slow functions; sorting by total time finds the expensive ones, and they are rarely the same list. A two-millisecond function is invisible in every trace of a single request and dominates a batch job, which is why profiling the job rather than extrapolating from a request is the only way to see it.