Performance difference between obj.function(...) and function(obj, ...) ?

54 points Dry-Horse-3150

I have simulation code that needs to run as fast as possible (too large to post to replicate results below unfortunately). Milliseconds count. As I have tuned the Rust code to maximize performance, I have found a surprising behavior.

Two functions, with identical body, but with signatures obj.function(...) and function(&obj, ...) have huge performance differences. This function is in the hot code path.

In my simulation, I run 3.2 trillion samples.

  • With obj.function(...), complete in 7.5 seconds, 501 million runs per second.
  • With function(&obj, ...), complete in 5.8 second, 550 million runs per second.

Since asked, a few other tidbits:

  • Yes, in release mode, lto=true, opt-level=3, codegen-units=1
  • Debian 12, headless machine, server CPU, fixed clock
  • No network activity, no disk activity, all in memory
  • Terminal test app
  • When simulation not running, average load is 0.00

I have run hundreds of trials and these are averages. That's a 10% difference simply from the function signature. No jitter analysis done, but seems statistically significant. Each variant reliably results in the same speed in each run. Small standard deviation, normal distribution. 501 and 550 are many SDs apart.

Is one not a sugaring of the other? Is there some compiler/hardware behavior that accounts for this?