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

Skip to content

Conversation

kzrnm
Copy link
Contributor

@kzrnm kzrnm commented Feb 16, 2024

In my previous pull request #92208, I split BigIntegerCalculator.Multiply into two methods: MultiplyNearLength and MultiplyFarLength.
However, this division resulted in a lot of code duplication.

The reason for this division is that coreLength can be greater than bits.Length - n. Actually, core.Slice(0, ActualLength(core)) seems to be sufficient since ActualLength(core)) is never greater than bits.Length - n.

https://github.com/kzrnm/dotnet-runtime/blob/f7eaf3b17fb4bd753b3404f0c9977c9693a2b480/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs#L282-L326

Benchmark

BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);

public class Tests
{
    public IEnumerable<object> GetMultiplyArgs()
    {
        var bytes = new byte[1000000];
        bytes.AsSpan().Fill(byte.MaxValue);
        var lengths = new int[] { 1000, 10000, 100000, 1000000 };
        for (int i = lengths.Length - 1; i >= 0; i--)
        {
            var largeLength = lengths[i];
            var large = Make(largeLength);
            foreach (var p in new double[] { 1, 0.75, 0.5, 0.25 })
            {
                var smallLength = (int)(p * lengths[i]);
                var small = Make(smallLength);
                yield return new Data($"{largeLength:D7}-{smallLength:D7}", large, small);
            }
        }
        BigInteger Make(int length)
        {
            return new BigInteger(bytes.AsSpan().Slice(0, length), isUnsigned: true);
        }
    }

    public record Data(string Name, BigInteger Large, BigInteger Small)
    {
        public override string ToString() => Name;
    }

    [Benchmark]
    [ArgumentsSource(nameof(GetMultiplyArgs))]
    public BigInteger Multiply(Data data)
    {
        return data.Large * data.Small;
    }
}

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3155/23H2/2023Update/SunValley3)
13th Gen Intel Core i5-13500, 1 CPU, 20 logical and 14 physical cores
.NET SDK 9.0.100-alpha.1.23615.4
  [Host]     : .NET 9.0.0 (9.0.23.61410), X64 RyuJIT AVX2
  Job-IVRPVB : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
  Job-VXHELJ : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2


Method Job Toolchain data Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
Multiply Job-IVRPVB \main\corerun.exe 0001000-0000250 9.472 μs 0.0781 μs 0.0731 μs 1.00 0.00 0.0916 - - 1.25 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0001000-0000250 8.178 μs 0.0726 μs 0.0644 μs 0.86 0.01 0.0916 - - 1.25 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0001000-0000500 13.684 μs 0.0564 μs 0.0528 μs 1.00 0.00 0.1068 - - 1.49 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0001000-0000500 12.726 μs 0.0695 μs 0.0650 μs 0.93 0.01 0.1068 - - 1.49 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0001000-0000750 18.784 μs 0.2849 μs 0.2665 μs 1.00 0.00 0.1221 - - 1.73 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0001000-0000750 18.205 μs 0.0999 μs 0.0935 μs 0.97 0.02 0.1221 - - 1.73 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0001000-0001000 20.274 μs 0.1754 μs 0.1640 μs 1.00 0.00 0.1526 - - 1.98 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0001000-0001000 20.543 μs 0.1343 μs 0.1257 μs 1.01 0.01 0.1526 - - 1.98 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0010000-0002500 365.931 μs 3.1887 μs 2.9827 μs 1.00 0.00 0.9766 - - 12.23 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0010000-0002500 353.082 μs 3.8782 μs 3.6277 μs 0.96 0.01 0.9766 - - 12.23 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0010000-0005000 538.574 μs 3.0983 μs 2.8982 μs 1.00 0.00 0.9766 - - 14.67 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0010000-0005000 522.857 μs 6.3931 μs 5.9801 μs 0.97 0.01 0.9766 - - 14.67 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0010000-0007500 738.188 μs 14.5480 μs 13.6082 μs 1.00 0.00 0.9766 - - 17.12 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0010000-0007500 727.770 μs 14.2161 μs 14.5989 μs 0.99 0.04 0.9766 - - 17.12 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0010000-0010000 782.050 μs 11.8638 μs 11.0974 μs 1.00 0.00 0.9766 - - 19.55 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0010000-0010000 798.392 μs 6.1704 μs 5.7718 μs 1.02 0.02 0.9766 - - 19.55 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0100000-0025000 14,109.077 μs 106.4570 μs 88.8964 μs 1.00 0.00 31.2500 31.2500 31.2500 122.12 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0100000-0025000 14,089.276 μs 126.7526 μs 118.5645 μs 1.00 0.01 31.2500 31.2500 31.2500 122.12 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0100000-0050000 21,654.298 μs 103.0624 μs 91.3621 μs 1.00 0.00 31.2500 31.2500 31.2500 146.53 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0100000-0050000 20,357.098 μs 162.0356 μs 143.6403 μs 0.94 0.01 31.2500 31.2500 31.2500 146.53 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0100000-0075000 28,971.251 μs 347.1229 μs 324.6990 μs 1.00 0.00 31.2500 31.2500 31.2500 170.96 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0100000-0075000 28,363.322 μs 210.1909 μs 196.6127 μs 0.98 0.01 31.2500 31.2500 31.2500 170.96 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 0100000-0100000 31,670.054 μs 217.2007 μs 203.1696 μs 1.00 0.00 31.2500 31.2500 31.2500 195.36 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 0100000-0100000 32,274.430 μs 520.1727 μs 486.5699 μs 1.02 0.02 - - - 195.34 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 1000000-0250000 552,147.007 μs 4,111.9758 μs 3,846.3447 μs 1.00 0.00 - - - 1220.79 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 1000000-0250000 547,253.633 μs 10,752.1365 μs 11,504.6656 μs 0.99 0.02 - - - 1221.45 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 1000000-0500000 849,144.546 μs 3,451.2756 μs 2,881.9714 μs 1.00 0.00 - - - 1464.93 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 1000000-0500000 806,284.180 μs 13,105.8916 μs 12,259.2592 μs 0.95 0.01 - - - 1464.93 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 1000000-0750000 1,124,194.693 μs 16,599.1463 μs 15,526.8517 μs 1.00 0.00 - - - 1709.12 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 1000000-0750000 1,069,443.869 μs 5,409.4313 μs 4,517.1201 μs 0.95 0.01 - - - 1709.12 KB 1.00
Multiply Job-IVRPVB \main\corerun.exe 1000000-1000000 1,286,760.120 μs 19,244.8875 μs 18,001.6797 μs 1.00 0.00 - - - 1953.87 KB 1.00
Multiply Job-VXHELJ \pr\corerun.exe 1000000-1000000 1,238,765.540 μs 10,636.9478 μs 9,949.8076 μs 0.96 0.02 - - - 1953.21 KB 1.00

@ghost ghost added the area-System.Numerics label Feb 16, 2024
@ghost
Copy link

ghost commented Feb 16, 2024

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Issue Details

In my previous pull request #92208, I split BigIntegerCalculator.Multiply into two methods: MultiplyNearLength and MultiplyFarLength.
However, this division resulted in a lot of code duplication.

The reason for this division is that coreLength can be greater than bits.Length - n. Actually, core.Slice(0, ActualLength(core)) seems to be sufficient since ActualLength(core)) is never greater than bits.Length - n.

https://github.com/kzrnm/dotnet-runtime/blob/371cc444826ab0667f5e76b8419bc621084f2fb2/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs#L322-L366

Benchmark

BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);

public class Tests
{
    public IEnumerable<object> GetMultiplyArgs()
    {
        Random random = new Random(227);
        var lengths = new int[] { 1000, 10000, 100000, 1000000 };
        for (int i = lengths.Length - 1; i >= 0; i--)
        {
            var largeBytes = MakeBytes(random, lengths[i]);
            var large = new BigInteger(largeBytes, isUnsigned: true);
            {
                var smallBytes = MakeBytes(random, lengths[i] / 2);
                var small = new BigInteger(smallBytes, isUnsigned: true);
                yield return new Data($"{largeBytes.Length:D7}-{smallBytes.Length:D7}", large, small);
            }
            for (int j = i; j >= 0; j--)
            {
                var smallBytes = MakeBytes(random, lengths[j]);
                var small = new BigInteger(smallBytes, isUnsigned: true);
                yield return new Data($"{largeBytes.Length:D7}-{smallBytes.Length:D7}", large, small);
            }
        }
        static byte[] MakeBytes(Random random, int length)
        {
            var bytes = new byte[length];
            random.NextBytes(bytes);
            return bytes;
        }
    }

    public record Data(string Name, BigInteger Large, BigInteger Small)
    {
        public override string ToString() => Name;
    }

    [Benchmark]
    [ArgumentsSource(nameof(GetMultiplyArgs))]
    public BigInteger Multiply(Data data)
    {
        return data.Large * data.Small;
    }
}

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3155/23H2/2023Update/SunValley3)
13th Gen Intel Core i5-13500, 1 CPU, 20 logical and 14 physical cores
.NET SDK 9.0.100-alpha.1.23615.4
  [Host]     : .NET 9.0.0 (9.0.23.61410), X64 RyuJIT AVX2
  Job-URSBLV : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
  Job-TQMTJC : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2


Method Job Toolchain data Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
Multiply Job-URSBLV \main\corerun.exe 0001000-0000500 13.12 μs 0.154 μs 0.137 μs 1.00 0.00 0.1068 - - 1.49 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0001000-0000500 13.25 μs 0.186 μs 0.174 μs 1.01 0.01 0.1068 - - 1.49 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0001000-0001000 20.37 μs 0.290 μs 0.271 μs 1.00 0.00 0.1526 - - 1.98 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0001000-0001000 19.59 μs 0.328 μs 0.307 μs 0.96 0.03 0.1526 - - 1.98 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0010000-0001000 206.28 μs 1.561 μs 1.384 μs 1.00 0.00 0.7324 - - 10.77 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0010000-0001000 208.83 μs 1.269 μs 1.125 μs 1.01 0.01 0.7324 - - 10.77 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0010000-0005000 513.09 μs 3.657 μs 3.420 μs 1.00 0.00 0.9766 - - 14.67 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0010000-0005000 535.01 μs 7.802 μs 7.298 μs 1.04 0.02 0.9766 - - 14.67 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0010000-0010000 816.73 μs 8.030 μs 7.511 μs 1.00 0.00 0.9766 - - 19.55 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0010000-0010000 814.75 μs 10.625 μs 9.939 μs 1.00 0.01 0.9766 - - 19.55 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0100000-0001000 2,146.54 μs 20.003 μs 18.711 μs 1.00 0.00 27.3438 27.3438 27.3438 98.67 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0100000-0001000 2,164.11 μs 17.230 μs 16.117 μs 1.01 0.01 27.3438 27.3438 27.3438 98.67 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0100000-0010000 8,421.19 μs 86.137 μs 80.572 μs 1.00 0.00 31.2500 31.2500 31.2500 107.47 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0100000-0010000 8,532.63 μs 79.239 μs 74.120 μs 1.01 0.01 31.2500 31.2500 31.2500 107.48 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0100000-0050000 20,429.37 μs 235.458 μs 220.247 μs 1.00 0.00 31.2500 31.2500 31.2500 146.55 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0100000-0050000 20,870.00 μs 267.251 μs 249.986 μs 1.02 0.01 31.2500 31.2500 31.2500 146.53 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 0100000-0100000 32,232.57 μs 634.088 μs 888.903 μs 1.00 0.00 - - - 195.38 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 0100000-0100000 30,761.56 μs 261.041 μs 244.178 μs 0.94 0.02 31.2500 31.2500 31.2500 195.36 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 1000000-0001000 21,984.70 μs 260.334 μs 243.516 μs 1.00 0.00 156.2500 156.2500 156.2500 977.67 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 1000000-0001000 21,369.03 μs 225.039 μs 210.502 μs 0.97 0.01 156.2500 156.2500 156.2500 977.68 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 1000000-0010000 89,806.51 μs 255.733 μs 199.660 μs 1.00 0.00 - - - 986.36 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 1000000-0010000 88,881.07 μs 878.241 μs 821.507 μs 0.99 0.01 - - - 986.42 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 1000000-0100000 348,310.20 μs 5,719.429 μs 5,349.957 μs 1.00 0.00 - - - 1074.3 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 1000000-0100000 337,064.95 μs 3,187.292 μs 2,981.395 μs 0.97 0.01 - - - 1074.3 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 1000000-0500000 829,713.72 μs 13,968.184 μs 13,065.848 μs 1.00 0.00 - - - 1464.93 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 1000000-0500000 815,624.28 μs 10,424.667 μs 9,751.240 μs 0.98 0.02 - - - 1465.59 KB 1.00
Multiply Job-URSBLV \main\corerun.exe 1000000-1000000 1,242,537.69 μs 15,210.633 μs 14,228.035 μs 1.00 0.00 - - - 1953.26 KB 1.00
Multiply Job-TQMTJC \pr\corerun.exe 1000000-1000000 1,233,080.07 μs 11,159.819 μs 10,438.902 μs 0.99 0.02 - - - 1953.26 KB 1.00
Author: kzrnm
Assignees: -
Labels:

area-System.Numerics

Milestone: -

@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from 80d294c to 763ebdd Compare February 17, 2024 00:34
@kzrnm kzrnm marked this pull request as draft February 17, 2024 03:04
@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from 763ebdd to f7eaf3b Compare February 17, 2024 04:29
@kzrnm kzrnm marked this pull request as ready for review February 17, 2024 04:30
@teo-tsirpanis teo-tsirpanis added the community-contribution Indicates that the PR has been added by a community member label Feb 17, 2024
@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from f7eaf3b to 7d4f903 Compare February 17, 2024 18:49
@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from 7d4f903 to 08ec62a Compare March 18, 2024 18:21
@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from 08ec62a to 95c7edc Compare March 22, 2024 22:38
@kzrnm kzrnm force-pushed the BigIntegerMultiplySlim branch from 95c7edc to f085edc Compare March 28, 2024 17:42
@tannergooding tannergooding merged commit 9eac175 into dotnet:main May 10, 2024
@kzrnm kzrnm deleted the BigIntegerMultiplySlim branch May 11, 2024 09:37
Ruihan-Yin pushed a commit to Ruihan-Yin/runtime that referenced this pull request May 30, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Jun 11, 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.

3 participants