2011-11-12 25 views
5

Estoy creando un código C#. El siguiente método es uno de los más caros. Para el propósito de esta pregunta, suponga que la micro-optimización es lo correcto. ¿Existe un enfoque para mejorar el rendimiento de este método?Optimizar C# Code Fragment

Cambiar el parámetro de entrada a p a crearía una macro ineficiencia.

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; 
    } 
} 
+3

Parece que BitConverter.ToInt64 - http://msdn.microsoft.com/en-us/library/system.bitconverter.toint64.aspx? –

+0

Lee y cambia algunos bytes. ¿Es sinceramente costoso? Estoy seguro de que lo está llamando mucho, pero me sorprendería si el compilador pudiera ir muy mal con ese – Rup

+1

@Alexei ToUInt64, pero sí. Si te refieres a usar eso en su lugar, publícalo como respuesta. (¿O quiere Eric optimizar BitConverter incluso?) – Rup

Respuesta

5

¿Por qué no utilizar BitConverter? Tengo que creer que Microsoft ha dedicado un tiempo a ajustar ese código. Además, trata con problemas endian.

Así es como BitConverter convierte un byte [] en una larga/ulong (ulong convierte como firmado y luego lo arroja a firmar):

[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)); 
    } 
} 

Sospecho que hacer la conversión de una palabra de 32 bits en un tiempo es para la eficiencia de 32 bits. No hay registros de 64 bits en una CPU de 32 bits significa que ocuparse de una entrada de 64 bits es mucho más caro.

Si está seguro de estar apuntando al hardware de 64 bits, puede ser más rápido hacer la conversión de una sola vez.

+0

También vale la pena señalar que esto está utilizando un código inseguro para mejorar el rendimiento; parece que el método BCL es la mejor opción. –

+0

D'oh! Usó BitConverter en un contexto diferente hoy y no pensó en eso. Por cierto, esta optimización mejoró el rendimiento general de mi puerto C# de CityHash en un 30% (lo que hace que ahora sea un 28% más rápido que la versión de C++ que porté). –

1

Como referencia, (Iniciativa de Código Compartido en http://referencesource.microsoft.com/netframework.aspx) de Microsoft .NET 4.0 BitConverter.ToInt64:

// 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

por qué no ir inseguro?

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