C#コードフラグメントを最適化する

私はいくつかのC#コードをプロファイリングしています。以下の方法は、最も高価なものの一つです。この質問の目的のために、マイクロ最適化を行うことが適切であると仮定します。この方法のパフォーマンスを向上させるアプローチはありますか?

入力パラメータを pulong [] に変更すると、マクロ非効率が発生します。

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は自分のコードよりもはるかに高速です
追加された 著者 Eric J.,
BitConverter.ToInt64のように見える - msdn.microsoft.com/en- us/library /&hellip;
追加された 著者 Alexei Levenkov,
数バイトの読み込みとシフト - それは正直に高価ですか?私はあなたがそれをたくさん呼んでいると確信していますが、コンパイラがそれに多くの間違いを犯す可能性があるなら、私は驚くでしょう
追加された 著者 Rup,
@Alexei ToUInt64、でもねえ。それを代わりに使用することを意味するのであれば、答えとして投稿してください。 (あるいは、EricはBitConverterを最適化したいのですか?)
追加された 著者 Rup,

4 答え

なぜBitConverterを使用しないのですか?私はマイクロソフトがそのコードを調整するのにある程度時間を費やしてきたと信じなければならない。さらに、エンディアンの問題も扱います。

BitConverterがbyte []をlong/ulongに変換する方法は次のとおりです(ulongは符号付きとして変換し、符号なしに変換します)。

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

私は一度に1つの32ビットワードを変換することが32ビット効率のためだと考えています。 32ビットCPU上の64ビットレジスタは、64ビットintを扱うことはずっと高価です。

64ビットハードウェアをターゲットにしていることが分かっている場合は、変換を一気に行うのが速いかもしれません。

5
追加された
D'oh!今日は別のコンテキストでBitConverterを使用していて、それについては考えていませんでした。ところで、この最適化はCityHashのC#ポート全体のパフォーマンスを30%向上させました(これは現在、移植されたC ++バージョンより28%速くなっています)。
追加された 著者 Eric J.,
パフォーマンスを向上させるために安全でないコードを使用していることにも注意してください。 BCLメソッドが最善の策だと思われます。
追加された 著者 Dan Bryant,

ループをアンロールするのではなく、 for を使用してみてください。境界チェックで時間を節約することができます。

BitConverter.ToUInt64を試してください - http://msdn.microsoft.com/ en-us/library/system.bitconverter.touint64.aspx を探してください。

2
追加された

なぜ安全でないの?

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

参考までに、Microsoftの.NET 4.0の BitConverter.ToInt64http: /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
追加された