Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigInteger constructor perf #91176

Merged
merged 6 commits into from
Nov 3, 2023
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -352,67 +352,60 @@ public BigInteger(ReadOnlySpan<byte> value, bool isUnsigned = false, bool isBigE
}
else
{
int unalignedBytes = byteCount % 4;
int dwordCount = byteCount / 4 + (unalignedBytes == 0 ? 0 : 1);
uint[] val = new uint[dwordCount];
int byteCountMinus1 = byteCount - 1;

// Copy all dwords, except don't do the last one if it's not a full four bytes
int curDword, curByte;
int wholeDwordCount = Math.DivRem(byteCount, 4, out int unalignedBytes);
Rob-Hague marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use:

(uint wholeUInt32Count, uint unalignedBytes) = Math.DivRem((uint)byteCount, 4);

instead, since we know here byteCount isn't negative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we did, then we would have to cast the values back to int in each of the AsSpan and Slice calls. What do you suggest?

uint[] val = new uint[wholeDwordCount + (unalignedBytes == 0 ? 0 : 1)];

// Copy all dwords, except don't do the last one if it's not a full four bytes.
// The dwords are stored in 'least significant first' order.
if (isBigEndian)
{
curByte = byteCount - sizeof(int);
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
for (int byteInDword = 0; byteInDword < 4; byteInDword++)
{
byte curByteValue = value[curByte];
val[curDword] = (val[curDword] << 8) | curByteValue;
curByte++;
}
// The bytes parameter is in big-endian byte order.
// We need to read the uints out in reverse.

curByte -= 8;
}
Span<byte> uintBytes = MemoryMarshal.AsBytes(val.AsSpan(0, wholeDwordCount));

// We need to slice off the remainder from the beginning.
value.Slice(unalignedBytes).CopyTo(uintBytes);

uintBytes.Reverse();
}
else
{
curByte = sizeof(int) - 1;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
for (int byteInDword = 0; byteInDword < 4; byteInDword++)
{
byte curByteValue = value[curByte];
val[curDword] = (val[curDword] << 8) | curByteValue;
curByte--;
}
// The bytes parameter is in little-endian byte order.
// We can just copy the bytes directly into the uint array.

curByte += 8;
}
value.Slice(0, wholeDwordCount * 4).CopyTo(MemoryMarshal.AsBytes<uint>(val));
}

// In both of the above cases on big-endian architecture, we need to perform
// an endianness swap on the resulting uints.
if (!BitConverter.IsLittleEndian)
{
BinaryPrimitives.ReverseEndianness(val.AsSpan(0, wholeDwordCount), val);
}

// Copy the last dword specially if it's not aligned
if (unalignedBytes != 0)
{
if (isNegative)
{
val[dwordCount - 1] = 0xffffffff;
val[wholeDwordCount] = 0xffffffff;
}

if (isBigEndian)
{
for (curByte = 0; curByte < unalignedBytes; curByte++)
for (int curByte = 0; curByte < unalignedBytes; curByte++)
{
byte curByteValue = value[curByte];
val[curDword] = (val[curDword] << 8) | curByteValue;
val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue;
}
}
else
{
for (curByte = byteCountMinus1; curByte >= byteCount - unalignedBytes; curByte--)
for (int curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
byte curByteValue = value[curByte];
val[curDword] = (val[curDword] << 8) | curByteValue;
val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue;
}
}
}
Expand Down