Optimaliseer C # codefragment

Ik ben een aantal C# -codes aan het profileren. De onderstaande methode is een van de duurste. Neem voor de toepassing van deze vraag aan dat micro-optimalisatie het juiste is om te doen. Bestaat er een aanpak om de prestaties van deze methode te verbeteren?

Het wijzigen van de invoerparameter in p in ulong [] zou een macro-inefficiëntie veroorzaken.

static ulong Fetch64(byte[] p, int ofs = 0)
{
    unchecked
    {
        ulong result = p[0 + ofs] + 
            ((ulong) p[1 + ofs] <<  8) + 
            ((ulong) p[2 + ofs] << 16) + 
            ((ulong) p[3 + ofs] << 24) + 
            ((ulong) p[4 + ofs] << 32) + 
            ((ulong) p[5 + ofs] << 40) + 
            ((ulong) p[6 + ofs] << 48) + 
            ((ulong) p[7 + ofs] << 56);
        return result;
    }
}
5
@Rup: BitConverter was veel sneller dan mijn code.
toegevoegd de auteur Eric J., de bron
Lijkt op BitConverter.ToInt64 - msdn.microsoft.com/en- ons/bibliotheek/& hellip; ?
toegevoegd de auteur Alexei Levenkov, de bron
Lees en verschuif een paar bytes - is dat echt duur? Ik weet zeker dat je het veel noemt, maar het zou me verbazen als de compiler daar veel mis mee zou kunnen doen
toegevoegd de auteur Rup, de bron
@Alexei ToUInt64, maar ja. Als je dat zou bedoelen, gebruik dan dat als een antwoord? (Of wil Eric zelfs BitConverter optimaliseren?)
toegevoegd de auteur Rup, de bron

4 antwoord

Waarom geen BitConverter gebruiken? Ik moet geloven dat Microsoft enige tijd heeft besteed aan het afstemmen van die code. Plus het behandelt endiaanse kwesties.

Hier is hoe BitConverter een byte [] omzet in een lange/ulong (ulong converteert het zoals ondertekend en werpt het vervolgens naar unsigned):

[SecuritySafeCritical]
public static unsafe long ToInt64(byte[] value, int startIndex)
{
  if (value == null)
  {
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
  }
  if (((ulong) startIndex) >= value.Length)
  {
    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
  }
  if (startIndex > (value.Length - 8))
  {
    ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
  }
  fixed (byte* numRef = &(value[startIndex]))
  {
    if ((startIndex % 8) == 0)
    {
      return *(((long*) numRef));
    }
    if (IsLittleEndian)
    {
      int num  = ((numRef[0] | (numRef[1] << 8)) | (numRef[2] << 0x10)) | (numRef[3] << 0x18);
      int num2 = ((numRef[4] | (numRef[5] << 8)) | (numRef[6] << 0x10)) | (numRef[7] << 0x18);
      return (((long) ((ulong) num)) | (num2 << 0x20));
    }
    int num3 = (((numRef[0] << 0x18) | (numRef[1] << 0x10)) | (numRef[2] << 8)) | numRef[3];
    int num4 = (((numRef[4] << 0x18) | (numRef[5] << 0x10)) | (numRef[6] << 8)) | numRef[7];
    return (((long) ((ulong) num4)) | (num3 << 0x20));
  }
}

Ik vermoed dat het doen van de omzetting één woord van 32 bits tegelijkertijd voor efficiency met 32 ​​bits is. Geen 64-bit registers op een 32-bit CPU betekent omgaan met een 64-bit-interface veel duurder.

Als u zeker weet dat u 64-bits hardware target, is het misschien sneller om de conversie in één klap uit te voeren.

5
toegevoegd
D'oh! Gebruikte BitConverter vandaag in een andere context en dacht daar niet aan. Tussen haakjes, deze optimalisatie verbeterde de algehele prestaties van mijn C# -poort van CityHash met 30% (waardoor het nu 28% sneller is dan de C ++ -versie waarvan ik heb geport).
toegevoegd de auteur Eric J., de bron
Het is ook vermeldenswaard dat dit onveilige code gebruikt om de prestaties te verbeteren; lijkt de BCL-methode de beste gok.
toegevoegd de auteur Dan Bryant, de bron

Probeer voor te gebruiken in plaats van de lus uit te rollen. U kunt mogelijk tijd besparen op grenscontroles.

Probeer BitConverter.ToUInt64 - http://msdn.microsoft.com/ en-us/library/system.bitconverter.touint64.aspx als dit is wat u zoekt.

2
toegevoegd

Waarom niet onveilig worden?

unsafe static ulong Fetch64(byte[] p, int ofs = 0)
{
  fixed (byte* bp = p)
  {
    return *((ulong*)(bp + ofs));
  }
}
1
toegevoegd

Ter referentie: Microsoft .NET 4.0 BitConverter.ToInt64 (initiatief voor gedeelde bronnen op http://referencesource.microsoft.com/netframework.aspx ):

   //Converts an array of bytes into a long.
    [System.Security.SecuritySafeCritical] //auto-generated 
    public static unsafe long ToInt64 (byte[] value, int startIndex) {
        if( value == null)  {
            ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
        } 

        if ((uint) startIndex >= value.Length) { 
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); 
        }

        if (startIndex > value.Length -8) {
            ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
        }

        fixed( byte * pbyte = &value[startIndex]) {
            if( startIndex % 8 == 0) {//data is aligned 
                return *((long *) pbyte); 
            }
            else { 
                if( IsLittleEndian) {
                    int i1 = (*pbyte) | (*(pbyte + 1) << 8)  | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
                    int i2  = (*(pbyte+4)) | (*(pbyte + 5) << 8)  | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24);
                    return (uint)i1 | ((long)i2 << 32); 
                }
                else { 
                    int i1 = (*pbyte << 24) | (*(pbyte + 1) << 16)  | (*(pbyte + 2) << 8) | (*(pbyte + 3)); 
                    int i2  = (*(pbyte+4) << 24) | (*(pbyte + 5) << 16)  | (*(pbyte + 6) << 8) | (*(pbyte + 7));
                    return (uint)i2 | ((long)i1 << 32); 
                }
            }
        }
    } 
1
toegevoegd