Conditional statement "!=" is not more gas efficient than ">" or ">="
Alleged Gas Costs
At the time of opening the issue, the Optimal Comparison Operator document makes the following claim:
In the case of a conditional statement, it is further optimal to use != when possible.
And then, the following gas costs are provided:
// 164 gas
function d() external pure {
require(1 > 0);
}
// 208 gas
function e() external pure {
require(1 >= 0);
}
// 120 gas
function f() external pure {
require(1 != 0);
}
Actual Gas Costs
In the first example below, each function execution will consume exactly 21,162 gas (with the function compare per se consuming exactly 98 gas).
Try these on Remix with the optimizer enabled and set to 200 runs
1. Require example
pragma solidity =0.8.17;
contract A {
function compare() external pure {
require(1 > 0);
}
}
contract B {
function compare() external pure {
require(1 >= 0);
}
}
contract C {
function compare() external pure {
require(1 != 0);
}
}
2. If/ else example
And in this case, the reported gasUsed will be 7.
pragma solidity =0.8.17;
contract A {
function compare(uint256 x) external view returns (uint256 gasUsed) {
uint256 startGas = gasleft();
if (x >= 0) {
uint256 foo = 1 + 2;
foo;
}
gasUsed = startGas - gasleft();
}
}
contract B {
function compare(uint256 x) external view returns (uint256 gasUsed) {
uint256 startGas = gasleft();
if (x > 0) {
uint256 foo = 1 + 2;
foo;
}
gasUsed = startGas - gasleft();
}
}
contract C {
function compare(uint256 x) external view returns (uint256 gasUsed) {
uint256 startGas = gasleft();
if (x != 0) {
uint256 foo = 1 + 2;
foo;
}
gasUsed = startGas - gasleft();
}
}
Possible Explanations
Let's start with this is what definitely isn't - this is not a matter of enabling the optimizer. Even with the optimizer disabled, the > 0 and != 0 checks cost the same in Solidity v0.8.17. That leaves with a couple of options left:
- You defined all functions in the same contract, which made the 4-byte function signature add extra gas, depending upon the results of the keccak256 hsah.
- Older compiler had different gas costs for these comparison operations? Unlikely, but possible.
- Some other unintentional benchmarking/ instrumenting mistake.
Whatever the case, as discussed in https://github.com/kadenzipfel/gas-optimizations/issues/3 and https://github.com/kadenzipfel/gas-optimizations/issues/15, the best way to avoid situations like this in the future would be to document the methodology that you used to obtain the reported gas costs, so that others can repeat your process and catch mistakes more easily.
Update: this conversation on Twitter is related to this issue.
As per transmission11's reply there, it does look like in an older version of Solidity, x != 0 used to be more gas efficient than x > 0.
Tagging @hrkrshnn in case he can illuminate us here?
I will follow up tomorrow.
Thanks @hrkrshnn, though no rush from my end.
@paulrberg The main difference is the use of via-ir. There are rules for transforming these expressions into an equivalent cheaper expression. But these rules are more effective in the new compilation pipeline. The legacy codegen and its optimizer works across basic blocks of assembly. And sometimes these expressions get split across basic blocks. See: https://hrkrshnn.com/t/devconnect.pdf#page=4 for another example.
I think these expressions should have the same cost if viaIR=true and the optimizer is enabled. Please don't hesitate to ping me again / open an issue in the original repo if that's not the case.
Thanks @hrkrshnn, makes sense.
What I don't understand though is that when I compiled the the code snippets above in Remix, I did not enable via-ir.
Is it that in the latest versions of Solidity, the compiler automatically applies via-ir to some operations, like != and >?
@paulrberg via-ir is not enabled by default yet. You can expect it to be default in an upcoming breaking release. The easiest way to test would be in Foundry, by adding viaIR = true in the toml config.
You can expect it to be default in an upcoming breaking release
Good to know!
via-ir is not enabled by default yet
This doesn't clear up the mystery yet of why the code snippets all yield the same gas cost even if --via-ir is not enabled.
This doesn't clear up the mystery yet of why the code snippets all yield the same gas cost even if --via-ir is not enabled.
It's likely because it uses different opcodes. See https://godbolt.org/z/q8K4WGqzr
Thank you ser, will take a look.