Skip to content

Commit

Permalink
Remove a bunch of array allocations (dotnet#82041)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub authored Feb 14, 2023
1 parent aab8ca0 commit 5584b26
Show file tree
Hide file tree
Showing 42 changed files with 331 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ internal static partial class Interop
internal static partial class Sys
{
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetControlCharacters")]
internal static partial void GetControlCharacters(
ControlCharacterNames[] controlCharacterNames, byte[] controlCharacterValues, int controlCharacterLength,
internal static unsafe partial void GetControlCharacters(
ControlCharacterNames* controlCharacterNames, byte* controlCharacterValues, int controlCharacterLength,
out byte posixDisableValue);

internal enum ControlCharacterNames : int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ internal static partial uint IcmpSendEcho2(SafeCloseIcmpHandle icmpHandle, SafeW
uint ipAddress, SafeLocalAllocHandle data, ushort dataSize, ref IP_OPTION_INFORMATION options, SafeLocalAllocHandle replyBuffer, uint replySize, uint timeout);

[LibraryImport(Interop.Libraries.IpHlpApi, SetLastError = true)]
internal static partial uint Icmp6SendEcho2(SafeCloseIcmpHandle icmpHandle, SafeWaitHandle Event, IntPtr apcRoutine, IntPtr apcContext,
byte[] sourceSocketAddress, byte[] destSocketAddress, SafeLocalAllocHandle data, ushort dataSize, ref IP_OPTION_INFORMATION options, SafeLocalAllocHandle replyBuffer, uint replySize, uint timeout);
internal static unsafe partial uint Icmp6SendEcho2(SafeCloseIcmpHandle icmpHandle, SafeWaitHandle Event, IntPtr apcRoutine, IntPtr apcContext,
byte* sourceSocketAddress, byte[] destSocketAddress, SafeLocalAllocHandle data, ushort dataSize, ref IP_OPTION_INFORMATION options, SafeLocalAllocHandle replyBuffer, uint replySize, uint timeout);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static class Huffman
{
// HPack static huffman code. see: https://httpwg.org/specs/rfc7541.html#huffman.code
// Stored into two tables to optimize its initialization and memory consumption.
private static readonly uint[] s_encodingTableCodes = new uint[257]
private static ReadOnlySpan<uint> EncodingTableCodes => new uint[257]
{
0b11111111_11000000_00000000_00000000,
0b11111111_11111111_10110000_00000000,
Expand Down Expand Up @@ -537,7 +537,7 @@ internal static class Huffman

public static (uint encoded, int bitLength) Encode(int data)
{
return (s_encodingTableCodes[data], EncodingTableBitLengths[data]);
return (EncodingTableCodes[data], EncodingTableBitLengths[data]);
}

private static ushort[] GenerateDecodingLookupTree()
Expand Down Expand Up @@ -574,7 +574,7 @@ private static ushort[] GenerateDecodingLookupTree()
// it is guaranteed that for this huffman code generated decoding lookup tree MUST consist of exactly 15 lookup tables
var decodingTree = new ushort[15 * 256];

uint[] encodingTableCodes = s_encodingTableCodes;
ReadOnlySpan<uint> encodingTableCodes = EncodingTableCodes;
ReadOnlySpan<byte> encodingTableBitLengths = EncodingTableBitLengths;

int allocatedLookupTableIndex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ private static byte[] DeriveKey(ReadOnlySpan<byte> exportedSessionKey, ReadOnlyS
makeNtlm2Hash(_credential.Domain, _credential.UserName, _credential.Password, ntlm2hash);

// Get random bytes for client challenge
byte[] clientChallenge = new byte[ChallengeLength];
Span<byte> clientChallenge = stackalloc byte[ChallengeLength];
RandomNumberGenerator.Fill(clientChallenge);

// Create empty LM2 response.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,14 @@ private static int CalcNumBuckets(ReadOnlySpan<int> hashCodes, bool optimizeForR
// In our precomputed primes table, find the index of the smallest prime that's at least as large as our number of
// hash codes. If there are more codes than in our precomputed primes table, which accommodates millions of values,
// give up and just use the next prime.
ReadOnlySpan<int> primes = HashHelpers.Primes;
int minPrimeIndexInclusive = 0;
while (minPrimeIndexInclusive < HashHelpers.s_primes.Length && codes.Count > HashHelpers.s_primes[minPrimeIndexInclusive])
while ((uint)minPrimeIndexInclusive < (uint)primes.Length && codes.Count > primes[minPrimeIndexInclusive])
{
minPrimeIndexInclusive++;
}

if (minPrimeIndexInclusive >= HashHelpers.s_primes.Length)
if (minPrimeIndexInclusive >= primes.Length)
{
return HashHelpers.GetPrime(codes.Count);
}
Expand All @@ -205,15 +206,15 @@ private static int CalcNumBuckets(ReadOnlySpan<int> hashCodes, bool optimizeForR

// Find the index of the smallest prime that accommodates our max buckets.
int maxPrimeIndexExclusive = minPrimeIndexInclusive;
while (maxPrimeIndexExclusive < HashHelpers.s_primes.Length && maxNumBuckets > HashHelpers.s_primes[maxPrimeIndexExclusive])
while ((uint)maxPrimeIndexExclusive < (uint)primes.Length && maxNumBuckets > primes[maxPrimeIndexExclusive])
{
maxPrimeIndexExclusive++;
}

if (maxPrimeIndexExclusive < HashHelpers.s_primes.Length)
if (maxPrimeIndexExclusive < primes.Length)
{
Debug.Assert(maxPrimeIndexExclusive != 0);
maxNumBuckets = HashHelpers.s_primes[maxPrimeIndexExclusive - 1];
maxNumBuckets = primes[maxPrimeIndexExclusive - 1];
}

const int BitsPerInt32 = 32;
Expand All @@ -227,7 +228,7 @@ private static int CalcNumBuckets(ReadOnlySpan<int> hashCodes, bool optimizeForR
for (int primeIndex = minPrimeIndexInclusive; primeIndex < maxPrimeIndexExclusive; primeIndex++)
{
// Get the number of buckets to try, and clear our seen bucket bitmap.
int numBuckets = HashHelpers.s_primes[primeIndex];
int numBuckets = primes[primeIndex];
Array.Clear(seenBuckets, 0, Math.Min(numBuckets, seenBuckets.Length));

// Determine the bucket for each hash code and mark it as seen. If it was already seen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal sealed class XmlUtil : IDisposable, IConfigErrorInfo

// Offset from where the reader reports the LinePosition of an Xml Node to
// the start of that representation in text.
private static readonly int[] s_positionOffset =
private static ReadOnlySpan<int> PositionOffset => new int[]
{
0, // None,
1, // Element, <elem
Expand Down Expand Up @@ -126,7 +126,7 @@ public void Dispose()

private static int GetPositionOffset(XmlNodeType nodeType)
{
return s_positionOffset[(int)nodeType];
return PositionOffset[(int)nodeType];
}

private void ReleaseResources()
Expand Down
7 changes: 4 additions & 3 deletions src/libraries/System.Console/src/System/ConsolePal.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -916,15 +916,16 @@ private static unsafe void EnsureInitializedCore()
Interop.Sys.SetTerminalInvalidationHandler(&InvalidateTerminalSettings);

// Load special control character codes used for input processing
var controlCharacterNames = new Interop.Sys.ControlCharacterNames[4]
const int NumControlCharacterNames = 4;
Interop.Sys.ControlCharacterNames* controlCharacterNames = stackalloc Interop.Sys.ControlCharacterNames[NumControlCharacterNames]
{
Interop.Sys.ControlCharacterNames.VERASE,
Interop.Sys.ControlCharacterNames.VEOL,
Interop.Sys.ControlCharacterNames.VEOL2,
Interop.Sys.ControlCharacterNames.VEOF
};
var controlCharacterValues = new byte[controlCharacterNames.Length];
Interop.Sys.GetControlCharacters(controlCharacterNames, controlCharacterValues, controlCharacterNames.Length, out s_posixDisableValue);
byte* controlCharacterValues = stackalloc byte[NumControlCharacterNames];
Interop.Sys.GetControlCharacters(controlCharacterNames, controlCharacterValues, NumControlCharacterNames, out s_posixDisableValue);
s_veraseCharacter = controlCharacterValues[0];
s_veolCharacter = controlCharacterValues[1];
s_veol2Character = controlCharacterValues[2];
Expand Down
66 changes: 26 additions & 40 deletions src/libraries/System.Data.Common/src/System/Xml/XPathNodePointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,32 @@ internal sealed class XPathNodePointer : IXmlDataVirtualNode
private DataColumn? _column;
private bool _fOnValue;
internal XmlBoundElement? _parentOfNS;
internal static readonly int[] s_xmlNodeType_To_XpathNodeType_Map = CreateXmlNodeTypeToXpathNodeTypeMap();
internal const string StrReservedXmlns = "http://www.w3.org/2000/xmlns/";
internal const string StrReservedXml = "http://www.w3.org/XML/1998/namespace";
internal const string StrXmlNS = "xmlns";
private bool _bNeedFoliate;

private static int[] CreateXmlNodeTypeToXpathNodeTypeMap()
internal static ReadOnlySpan<int> XmlNodeTypeToXpathNodeTypeMap => new int[]
{
#if DEBUG
int max = 0, tempVal = 0;
XmlNodeType[] enumValues = Enum.GetValues<XmlNodeType>();
for (int i = 0; i < enumValues.Length; i++)
{
tempVal = (int)enumValues[i];
if (tempVal > max)
max = tempVal;
}
Debug.Assert(max == (int)XmlNodeType.XmlDeclaration);
#endif
var map = new int[20];
map[(int)XmlNodeType.None] = -1;
map[(int)XmlNodeType.Element] = (int)XPathNodeType.Element;
map[(int)XmlNodeType.Attribute] = (int)XPathNodeType.Attribute;
map[(int)XmlNodeType.Text] = (int)XPathNodeType.Text;
map[(int)XmlNodeType.CDATA] = (int)XPathNodeType.Text;
map[(int)XmlNodeType.EntityReference] = -1;
map[(int)XmlNodeType.Entity] = -1;
map[(int)XmlNodeType.ProcessingInstruction] = (int)XPathNodeType.ProcessingInstruction;
map[(int)XmlNodeType.Comment] = (int)XPathNodeType.Comment;
map[(int)XmlNodeType.Document] = (int)XPathNodeType.Root;
map[(int)XmlNodeType.DocumentType] = -1;
map[(int)XmlNodeType.DocumentFragment] = (int)XPathNodeType.Root;
map[(int)XmlNodeType.Notation] = -1;
map[(int)XmlNodeType.Whitespace] = (int)XPathNodeType.Whitespace;
map[(int)XmlNodeType.SignificantWhitespace] = (int)XPathNodeType.SignificantWhitespace;
map[(int)XmlNodeType.EndElement] = -1;
map[(int)XmlNodeType.EndEntity] = -1;
map[(int)XmlNodeType.XmlDeclaration] = -1;
// xmlNodeType_To_XpathNodeType_Map[(int)(XmlNodeType.All)] = -1;
return map;
}
/*XmlNodeType.None*/ -1,
/*XmlNodeType.Element*/ (int)XPathNodeType.Element,
/*XmlNodeType.Attribute*/ (int)XPathNodeType.Attribute,
/*XmlNodeType.Text*/ (int)XPathNodeType.Text,
/*XmlNodeType.CDATA*/ (int)XPathNodeType.Text,
/*XmlNodeType.EntityReference*/ -1,
/*XmlNodeType.Entity*/ -1,
/*XmlNodeType.ProcessingInstruction*/ (int)XPathNodeType.ProcessingInstruction,
/*XmlNodeType.Comment*/ (int)XPathNodeType.Comment,
/*XmlNodeType.Document*/ (int)XPathNodeType.Root,
/*XmlNodeType.DocumentType*/ -1,
/*XmlNodeType.DocumentFragment*/ (int)XPathNodeType.Root,
/*XmlNodeType.Notation*/ -1,
/*XmlNodeType.Whitespace*/ (int)XPathNodeType.Whitespace,
/*XmlNodeType.SignificantWhitespace*/ (int)XPathNodeType.SignificantWhitespace,
/*XmlNodeType.EndElement*/ -1,
/*XmlNodeType.EndEntity*/ -1,
/*XmlNodeType.XmlDeclaration*/ -1,
};

private XPathNodeType DecideXPNodeTypeForTextNodes(XmlNode node)
{
Expand Down Expand Up @@ -88,10 +73,10 @@ private XPathNodeType DecideXPNodeTypeForTextNodes(XmlNode node)

private XPathNodeType ConvertNodeType(XmlNode node)
{
int xnt;
if (XmlDataDocument.IsTextNode(node.NodeType))
return DecideXPNodeTypeForTextNodes(node);
xnt = s_xmlNodeType_To_XpathNodeType_Map[(int)(node.NodeType)];

int xnt = XmlNodeTypeToXpathNodeTypeMap[(int)(node.NodeType)];
if (xnt == (int)XPathNodeType.Attribute)
{
if (node.NamespaceURI == StrReservedXmlns)
Expand Down Expand Up @@ -695,10 +680,11 @@ internal bool MoveToNextAttribute(bool bFirst)

private static bool IsValidChild(XmlNode parent, XmlNode child)
{
int xntChildInt = s_xmlNodeType_To_XpathNodeType_Map[(int)(child.NodeType)];
int xntChildInt = XmlNodeTypeToXpathNodeTypeMap[(int)(child.NodeType)];
if (xntChildInt == -1)
return false;
int xntInt = s_xmlNodeType_To_XpathNodeType_Map[(int)(parent.NodeType)];

int xntInt = XmlNodeTypeToXpathNodeTypeMap[(int)(parent.NodeType)];
Debug.Assert(xntInt != -1);
return xntInt switch
{
Expand All @@ -721,7 +707,7 @@ private static bool IsValidChild(XmlNode parent, XmlNode child)

private static bool IsValidChild(XmlNode parent, DataColumn c)
{
int xntInt = s_xmlNodeType_To_XpathNodeType_Map[(int)(parent.NodeType)];
int xntInt = XmlNodeTypeToXpathNodeTypeMap[(int)(parent.NodeType)];
Debug.Assert(xntInt != -1);
return xntInt switch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3008,13 +3008,17 @@ internal static bool IsOnlyWhitespace( string str ) {
protected override XPathNavigator? CreateNavigator(XmlNode node)
{
Debug.Assert(node.OwnerDocument == this || node == this);
if (XPathNodePointer.s_xmlNodeType_To_XpathNodeType_Map[(int)(node.NodeType)] == -1)

if (XPathNodePointer.XmlNodeTypeToXpathNodeTypeMap[(int)(node.NodeType)] == -1)
return null;

if (IsTextNode(node.NodeType))
{
XmlNode? parent = node.ParentNode;
if (parent != null && parent.NodeType == XmlNodeType.Attribute)
{
return null;
}
else
{
#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
Expand Down Expand Up @@ -375,17 +376,22 @@ public static int[] GetProcessIds(string machineName, bool isRemoteMachine)

public static int[] GetProcessIds()
{
int[] processIds = new int[256];
int[] processIds = ArrayPool<int>.Shared.Rent(256);

int needed;
while (true)
{
int size = processIds.Length * sizeof(int);
if (!Interop.Kernel32.EnumProcesses(processIds, size, out needed))
{
throw new Win32Exception();
}

if (needed == size)
{
processIds = new int[processIds.Length * 2];
int newLength = processIds.Length * 2;
ArrayPool<int>.Shared.Return(processIds);
processIds = ArrayPool<int>.Shared.Rent(newLength);
continue;
}

Expand All @@ -394,6 +400,8 @@ public static int[] GetProcessIds()

int[] ids = new int[needed / sizeof(int)];
Array.Copy(processIds, ids, ids.Length);

ArrayPool<int>.Shared.Return(processIds);
return ids;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,18 @@ public HuffmanTree(byte[] codeLengths)
private static byte[] GetStaticLiteralTreeLength()
{
byte[] literalTreeLength = new byte[MaxLiteralTreeElements];
for (int i = 0; i <= 143; i++)
literalTreeLength[i] = 8;

for (int i = 144; i <= 255; i++)
literalTreeLength[i] = 9;

for (int i = 256; i <= 279; i++)
literalTreeLength[i] = 7;

for (int i = 280; i <= 287; i++)
literalTreeLength[i] = 8;

literalTreeLength.AsSpan(0, 144).Fill(8);
literalTreeLength.AsSpan(144, 112).Fill(9);
literalTreeLength.AsSpan(256, 24).Fill(7);
literalTreeLength.AsSpan(280, 8).Fill(8);
return literalTreeLength;
}

private static byte[] GetStaticDistanceTreeLength()
{
byte[] staticDistanceTreeLength = new byte[MaxDistTreeElements];
for (int i = 0; i < MaxDistTreeElements; i++)
{
staticDistanceTreeLength[i] = 5;
}
Array.Fill(staticDistanceTreeLength, (byte)5);
return staticDistanceTreeLength;
}

Expand All @@ -124,14 +114,16 @@ private static uint BitReverse(uint code, int length)
// This algorithm is described in standard RFC 1951
private uint[] CalculateHuffmanCode()
{
uint[] bitLengthCount = new uint[17];
Span<uint> bitLengthCount = stackalloc uint[17];
bitLengthCount.Clear();
foreach (int codeLength in _codeLengthArray)
{
bitLengthCount[codeLength]++;
}
bitLengthCount[0] = 0; // clear count for length 0

uint[] nextCode = new uint[17];
Span<uint> nextCode = stackalloc uint[17];
nextCode.Clear();
uint tempCode = 0;
for (int bits = 1; bits <= 16; bits++)
{
Expand Down
3 changes: 0 additions & 3 deletions src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ System.IO.Hashing.XxHash32</PackageDescription>
<Compile Include="System\IO\Hashing\XxHash64.State.cs" />
<Compile Include="System\IO\Hashing\XxHashShared.cs" />
<Compile Include="System\IO\Hashing\NonCryptographicHashAlgorithm.cs" />
<Compile Include="$(CommonPath)System\Numerics\Crc32ReflectedTable.cs">
<Link>Common\System\Numerics\Crc32ReflectedTable.cs</Link>
</Compile>
<Compile Include="System\IO\Hashing\BitOperations.cs"
Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
</ItemGroup>
Expand Down
Loading

0 comments on commit 5584b26

Please sign in to comment.