RFR: 8319423: Improve Year.isLeap by checking divisibility by 16 [v2]
Cassio Neri
duke at openjdk.org
Sat Nov 4 21:38:08 UTC 2023
On Sat, 4 Nov 2023 16:01:44 GMT, Claes Redestad <redestad at openjdk.org> wrote:
>> src/java.base/share/classes/java/time/Year.java line 321:
>>
>>> 319: // So for a year that's divisible by 4, checking that it's also divisible by 16
>>> 320: // is sufficient to determine it must be a leap year.
>>> 321: return (year & 15) == 0 ? (year & 3) == 0 : (year & 3) == 0 && year % 100 != 0;
>>
>> I think `(year & 3) == 0 && ((year & 15) == 0) || (year % 25) != 0` would be better simply because the common path will be a little bit shorter.
>
> Benchmark Mode Cnt Score Error Units
> LeapYearBench.isLeapYear thrpt 15 0,735 ± 0,004 ops/us
> LeapYearBench.isLeapYearChrono thrpt 15 0,734 ± 0,006 ops/us
>
>
> So equal to or even slightly worse than baseline. I tested a few variants before submitting the PR - some that looked simpler or better - but the ternary variant in this PR always came out on top.
On top of my general comments made earlier, let me give my two cents on this line:
return (year & 15) == 0 ? (year & 3) == 0 : (year & 3) == 0 && year % 100 != 0;
If `(year & 15) == 0`, then the last *four* bits of `year` are zeros. In particular, the last *two* bits of `year` are zeros, that is, `(year & 3) == 0`. The same conclusion can be obtained in terms of divisibility: if `year % 16 == 0`, i.e., `year` is a multiple of 16, then `year % 4 == 0`, i.e., `year` is multiple of 4. What I'm trying to say is that the line above can be simplified to:
return (year & 15) == 0 ? true : (year & 3) == 0 && year % 100 != 0;
But now it becomes clear that the above is also equivalent to:
return (year & 15) == 0 || ((year & 3) == 0 && year % 100 != 0);
Which is the simplest form of all the above. It's possible, but I'm not sure, that the Java compiler makes this simplification for you. (FWIW: the GCC C compiler does. Indeed, as seen [here](https://godbolt.org/z/P8sMv3Yno) the three expressions above generate exactly the same assembly instructions.)
As I explained in my earlier post, for this particular expression a further simplification, that makes the compiler (at least the C compiler [above](https://godbolt.org/z/P8sMv3Yno)) to save one instruction (`ror edi 2`) is replacing 100 by 25:
return (year & 15) == 0 || ((year & 3) == 0 && year % 25 != 0);
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/16491#discussion_r1382469177
More information about the core-libs-dev
mailing list