RFR: 8234331: Add robust and optimized utility for rounding up to next power of two+

Claes Redestad claes.redestad at oracle.com
Wed Dec 4 17:37:05 UTC 2019


Hi Thomas,

On 2019-12-04 17:32, Thomas Stüfe wrote:
> Hi Claes,
> 
> I think this is very good and useful.
> 
> General remarks:
> 
> - Pity that it only works for 32/64bit values. It would be neat to have 
> variants for 8 and 16 bit values too.

I don't think adding 8- and 16-bit variants would be too hard, but would
need specialized adjustment since most platforms only have 32- and 64-
bit intrinsics for clz. Do you know any code where we might need such
specializations...?

> 
> - +1 for a "lumpier" header.

o_O

> 
> - the round_down() variants: would it make sense to allow 0 as valid 
> input? I can imagine scenarios where 0 could be a valid input and result 
> for this operation.

I've been contemplating if there's room for variants that are less
strict, e.g., where you can define what values you should get on
0 or overflow. I think that's out of scope for this RFE.

> 
> - I tested on AIX, builds and gtests run through without errors.

Great, thanks!

> 
> ----
> 
> 
> http://cr.openjdk.java.net/~redestad/8234331/open.03/src/hotspot/share/runtime/threadSMR.cpp.udiff.html
> 
> +  int hash_table_size = 
> round_up_power_of_2(MIN2((int)get_java_thread_list()->length(), 32) << 1);
> 
> Style nit, I would have preferred this to be two operations for 
> readability.

Sure, will fix.

> 
> 
> http://cr.openjdk.java.net/~redestad/8234331/open.03/src/hotspot/share/utilities/powerOfTwo.hpp.html
> 
>    43 inline typename EnableIf<IsSigned<T>::value, T>::type 
> round_down_power_of_2(T value) {
> ...
> 
>    47   assert(lz < sizeof(T) * BitsPerByte, "Sanity");
> 
> Do we need this if we check the input value for >0 ? (Same question for 
> the signed version of round_up() below).

Technically we shouldn't need any of the "Sanity" asserts that check
that lz is in the expected range ([0-31]/[0-63]). I added them mostly as
scaffolding when writing these functions, but kept them since I think
they help reason about the code. I can remove them if you insist.

> 
>    48   assert(lz > 0, "Overflow to negative");
> 
> Same here?

Right, for round_down this one is pointless lz == 0 would only be
possible if value > 0. I'll remove this one.

For round_up we overflow when value > 2^30, so lz == 1, which is not
covered by value > 0. So that assert I think is needed.

> Also, small nit, the assert text is slightly off since no overflow 
> happened yet, but the input value is negative.

Maybe just:

   assert(lz > 1, "Will overflow");

> 
> (The asserts do not bother me much, I am just trying to understand why 
> we need them)

To catch obvious programming mistakes, catch more issues during testing,
and reason about the code.

> 
> -
> 
>   100 // Accepts 0 (returns 1); overflows if value is larger than or 
>   equal to 2^31
>   101 // or 2^63, for 32- and 64-bit integers, respectively
> 
> Comment is a bit off. True only for unsigned values. Also it should 
> mention that overflow results in assert to be in sync with the other 
> comments.

How about:

// Accepts 0 (returns 1), overflows with assert if value is larger than
// or equal to 2^31 (2^30 for signed) or 2^63 (2^62 if signed), for 32-
// and 64-bit integers, respectively

> 
> ----
> 
> http://cr.openjdk.java.net/~redestad/8234331/open.03/src/hotspot/share/utilities/count_leading_zeros.hpp.frames.html
> 
> template <typename T> inline uint32_t count_leading_zeros(T x) {
> 
> Instead of all the "if sizeof(T) == 4|8", could we specialize the 
> functions for different operand sizes? Example:
> 
> template <typename T, size_t n> struct Impl;
> template <typename T> struct Impl<T, 8> { static int doit(T v) { return 
> __builtin_clzll(v); } };
> template <typename T> struct Impl<T, 4> { static int doit(T v) { return 
> __builtin_clz(v); } };
> template <typename T> int count_leading_zero(T v) { return Impl<T, 
> sizeof(T)>::doit(v); }
> 
> Would make it easier later to plug in implementations for different 
> sizes, and we'd still get a compile time error for invalid input types.

I guess we could, but I'm not so sure it'd help readability.

> 
> -
> 
> I wondered why count_leading_zeros did not allow 0 as input until I saw 
> that __builtin_clz(ll) unfolds to bsr; and that does not work for 
> input=0. I still find it an artificial limitation but that was there 
> before your patch.

Yes, it's a bit unfortunate and artificial, and I think we'd regress
some actually performance-critical places by adding a special-case for
0. If we want graceful versions that give 0 and max on over- and
underflow we can add them in separately, I think.

> 
> ----
> 
> Thank you for the enhanced comment in the fallback implementation, thats 
> helpful.
> 
> ----
> 
> http://cr.openjdk.java.net/~redestad/8234331/open.03/test/hotspot/gtest/utilities/test_powerOfTwo.cpp.html
> 
> It bothers me a bit that we do not test invalid input and overflow. 
> There is a way, I believe, to have gtests expect an assert. We have 
> TEST_VM_ASSERT but I am not sure if that is a good idea since crashing 
> takes time. I also see that we only use it at a single place, so I guess 
> others share the same concern :)
> 
> Alternatively, one could have variants of round_up/down which return a 
> definite value on overflow. I wonder whether this may be useful 
> someplace outside the tests.

Yeah, a lighter mechanism for testing asserts would be great. I'd prefer
deferring negative tests to a follow-up, though, since I have a few
other things to attend to before 14 FC... :-)

> 
> -
> 
> Just a thought, but could you cut down the code for the many TEST 
> functions if you would use a templatized test function? Basically, 
> something like
> 
> template <class T> struct MyMaxImpl;
> template <int32_t> MyMaxImpl   { static int32 get() { return 
> int32_max_pow2; } };
> .. etc
> 
> template <typename T> void round_up_test() {
>    EXPECT_EQ(round_up_power_of_2((T)1), (T)1) << "value = " << 1;
> ...
>      const T max = MyMaxImpl<T>::get();
>    EXPECT_EQ(round_up_power_of_2(max - 1), max)
> ...
> }
> 
> TEST(power_of_2, round_up_power_of_2_signed_32) {
>    round_up_do_test<int32_t>();
> }
> 
> That way the test would also be easily extendable later if we decide to 
> add support for uint8_t or uint16_t.

We might save a few lines of code, yes, but my preference is keeping
tests explicit even if that makes them somewhat more repetitive - to a
point. I can see if we were to add int8/int16 variants that this might
move well beyond that point, though.

Thanks!

/Claes


More information about the hotspot-compiler-dev mailing list