Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Rob-Hague
Copy link
Contributor

@Rob-Hague Rob-Hague commented Jul 6, 2024

This path is taken once we know the most significant uint is itself a power of two, and we just need to check the rest are zero. The benchmarks are an attempt to show the worst-case:

Method Job Mean Error StdDev Ratio RatioSD
SmallPowerOfTwo main 1.436 ns 0.0010 ns 0.0009 ns 1.00 0.00
SmallPowerOfTwo PR 2.503 ns 0.0469 ns 0.0392 ns 1.74 0.03
SmallNotPowerOfTwo main 1.117 ns 0.0012 ns 0.0011 ns 1.00 0.00
SmallNotPowerOfTwo PR 2.464 ns 0.0063 ns 0.0059 ns 2.21 0.01
LargePowerOfTwo main 31.979 ns 0.0081 ns 0.0072 ns 1.00 0.00
LargePowerOfTwo PR 5.711 ns 0.0028 ns 0.0025 ns 0.18 0.00
LargeNotPowerOfTwo main 1.119 ns 0.0011 ns 0.0009 ns 1.00 0.00
LargeNotPowerOfTwo PR 2.919 ns 0.0011 ns 0.0009 ns 2.61 0.00
public class Benchmarks
{
    private BigInteger _smallPowerOfTwo;
    private BigInteger _smallNotPowerOfTwo;
    private BigInteger _largePowerOfTwo;
    private BigInteger _largeNotPowerOfTwo;

    [GlobalSetup]
    public void GlobalSetup()
    {
        _smallPowerOfTwo    = BigInteger.Parse("0000000000000001000000000000000000000000000000000000000000000000", NumberStyles.BinaryNumber);
        _smallNotPowerOfTwo = BigInteger.Parse("0000000000000001000000000000000001110011011011000010100011101111", NumberStyles.BinaryNumber);

        Random rnd = new Random(123456);
        
        var bytes = new byte[1 + 256];
        bytes[0] = 1;
        
        _largePowerOfTwo = new BigInteger(bytes, isBigEndian: true);
        
        rnd.NextBytes(bytes.AsSpan(1));

        _largeNotPowerOfTwo = new BigInteger(bytes, isBigEndian: true);
    }

    [Benchmark]
    public bool SmallPowerOfTwo() => _smallPowerOfTwo.IsPowerOfTwo;
    
    [Benchmark]
    public bool SmallNotPowerOfTwo() => _smallNotPowerOfTwo.IsPowerOfTwo;
    
    [Benchmark]
    public bool LargePowerOfTwo() => _largePowerOfTwo.IsPowerOfTwo;
    
    [Benchmark]
    public bool LargeNotPowerOfTwo() => _largeNotPowerOfTwo.IsPowerOfTwo;
}

@ghost ghost added the area-System.Numerics label Jul 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jul 6, 2024
@danmoseley
Copy link
Member

Thanks. Are the regressions worth it?

@Rob-Hague
Copy link
Contributor Author

Thanks. Are the regressions worth it?

IndexOfAnyExcept (now ContainsAnyExcept) is "obviously" better in lots of cases, so I focused the benchmarks on where it isn't. I don't feel strongly about the change, but my thinking is:

  1. If you have some code which is checking for powers of two, then there is a priori some likelihood that the input domain contains powers of two, in which case this is likely to be an improvement.
  2. If the upper 32 bits of a value is already a power of two (popcount == 1), as we have determined before this path, then the likelihood is further increased for this particular value.
  3. If someone cares about the performance in the small (~64 bit) case, then they should rather be using e.g. ulong or UInt128.

@stephentoub stephentoub enabled auto-merge (squash) July 9, 2024 15:31
@stephentoub stephentoub merged commit ce2f85b into dotnet:main Jul 9, 2024
@Rob-Hague Rob-Hague deleted the bigintpowerof2 branch July 9, 2024 16:24
matouskozak added a commit to matouskozak/runtime that referenced this pull request Jul 11, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Aug 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Numerics community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants