Here is my code.
contract Demo {
function add1(uint256 a, uint256 b) public pure returns (uint256) {
if (a==0) {
return 0;
}
uint256 c = a + b;
return c;
}
}
contract Test {
Demo demo = new Demo();
function testGas() public {
uint256 startGas = gasleft();
demo.add1(1, 2);
uint256 endGas = gasleft();
uint256 gasUsage1 = startGas - endGas;
startGas = gasleft();
demo.add1(1, 2);
endGas = gasleft();
uint256 gasUsage2 = startGas - endGas;
}
}
The gasUsage1 is 6434 and gasUsage2 is 1919.
When the function is called a third time, the gas usage is the same as the second time.
I tested other functions and the results were the same.
There are two main contributions: reading the address from storage, and then touching it.
By doing demo.add1(1, 2);
the code first needs to read (SLOAD) the address of demo
saved in storage. The gas cost is 2100 the first time (cold access), then 100 (warm access). docs
Calling the demo contract will "touch" its address. The gas cost the first time (cold access) is 2600, then its 100. docs
This explain a 2000+2500 = 4500 difference.
So, 6434-(1919+4500) = 15. There's only 15 extra gas, that's probably due to stack/memory operations.