web-dev-qa-db-ja.com

C#でintを4バイトに変換する最速の方法

C#でintを4バイトに変換する最速の方法は何ですか?

開発時間ではなく実行時間のように最速。

私自身の解決策はこのコードです:

byte[] bytes = new byte[4];
unchecked
{
 bytes[0] = (byte)(data >> 24);
 bytes[1] = (byte)(data >> 16);
 bytes[2] = (byte)(data >> 8);
 bytes[3] = (byte)(data);
}

今のところ、私のソリューションはstructBitConverterの両方を数ティック上回っています。

安全でないことがおそらく最速のオプションだと思い、それを答えとして受け入れますが、私は管理されたオプションを使用したいと思います。

19
MichaelT

安全でないコードを使用したバイト*キャストは、はるかに高速です。

    unsafe static void Main(string[] args) {
        int i = 0x12345678;
        byte* pi = (byte*)&i;
        byte lsb = pi[0];  
        // etc..
    }

これはBitConverterも同様に行うことであり、このコードは配列を作成するコストを回避します。

15
Hans Passant

C#でintを4バイトに変換する最速の方法は何ですか?

BitConverter を使用すると、32ビット整数を使用する GetBytes オーバーロードになります。

int i = 123;
byte[] buffer = BitConverter.GetBytes(i);
16
Darin Dimitrov

基本型をバイト配列にシリアル化するのに必要な時間を調査しました。データを配置する配列とオフセットがすでにある場合に使用しました。理論的には4バイトの配列を取得するのと比較して、これは本当に重要なケースだと思います。何かをシリアル化するとき、それがまさに必要なものだからです。どのメソッドがより高速であるかに対する答えは、シリアル化するタイプによって異なることがわかりました。私はいくつかの方法を試しました:

  1. 追加のバッファオーバーランチェックを伴う安全でない参照
  2. GetBytes +結果のBuffer.BulkCopy(これは基本的に1 +オーバーヘッドと同じです)
  3. シフトによる直接割り当て(m_Bytes[offset] = (byte)(value >> 8)
  4. シフトとビット単位の直接割り当て&m_Bytes[offset] = (byte)((i >> 8) & 0xFF)

私はすべてのテストを10百万回実行しました。以下はミリ秒単位の結果です

 Long Int Short Byte Float Double 
 1 29 32 31 30 29 34 
 2 209233220212208228 
 3 63 24 13 8 24 44 
 4 72 29 14 

ご覧のとおり、安全でない方法はlongとdoubleの方がはるかに高速です(署名されていないバージョンは署名されたバージョンとほぼ同じであるため、表には含まれていません)。 short/int/floatの場合、最速の方法はシフト付きの2/4/4割り当てです。バイトの場合、最速は明らかに単純な割り当てです。したがって、元の質問に関しては、割り当て方法が最適です。これは、このような関数の最速の例です。

    public static void WriteInt(byte[] buffer, int offset, int value)
    {
        m_BytesInt[offset] = (byte)(value >> 24);
        m_BytesInt[offset + 1] = (byte)(value >> 16);
        m_BytesInt[offset + 2] = (byte)(value >> 8);
        m_BytesInt[offset + 3] = (byte) value;
    }

P.S.テストは、リリースモードでcpu any(実行時にはx64)にコンパイルされたコードを使用してx64環境で実行されました。

10

最速の方法は、4バイトを含む構造体を使用することです。

  • 定義されたレイアウト(バイト位置0、1、2、3)
  • そして、位置0から始まるint32。
  • 4つの変数を入力し、バイトを読み取ります。
  • 終了しました。

BitConverterよりも大幅に高速です。

http://msdn.Microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx

必要な属性があります。

[StructLayout(LayoutKind.Explicit)]
struct FooUnion
{
    [FieldOffset(0)]
    public byte byte0;
    [FieldOffset(1)]
    public byte byte1;
    [FieldOffset(2)]
    public byte byte2;
    [FieldOffset(3)]
    public byte byte3;

    [FieldOffset(0)]
    public int integer;

}
9
TomTom

以下のテストが示すように、BitConverterが最速ではない可能性があることに注意してください。

BitConverter クラス、具体的には GetBytes メソッドを使用して Int32 パラメータ:

var myInt = 123;
var bytes = BitConverter.GetBytes(myInt);

BitConverter.IsLittlEndian CPUアーキテクチャに基づいてバイト順序を決定します。


編集:コンパイラの最適化のため、以下のテストは決定的ではありません。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [StructLayout(LayoutKind.Explicit)]
    struct FooUnion
    {
        [FieldOffset(0)]
        public byte byte0;
        [FieldOffset(1)]
        public byte byte1;
        [FieldOffset(2)]
        public byte byte2;
        [FieldOffset(3)]
        public byte byte3;

        [FieldOffset(0)]
        public int integer;
    }
    class Program
    {
        static void Main(string[] args)
        {
            testUnion();
            testBitConverter();

            Stopwatch Timer = new Stopwatch();

            Timer.Start();
            testUnion();
            Timer.Stop();

            Console.WriteLine(Timer.ElapsedTicks);

            Timer = new Stopwatch();

            Timer.Start();
            testBitConverter();
            Timer.Stop();

            Console.WriteLine(Timer.ElapsedTicks);
            Console.ReadKey();
        }

        static void testBitConverter()
        {
            byte[] UnionBytes;

            for (int i = 0; i < 10000; i++)
            {
                UnionBytes = BitConverter.GetBytes(i);
            }
        }

        static void testUnion()
        {
            byte[] UnionBytes;

            for (int i = 0; i < 10000; i++)
            {
                FooUnion union = new FooUnion() { integer = i };

                UnionBytes = new byte[] { union.byte0, union.byte1, union.byte2, union.byte3 };

            }
        }
    }
}
7
George Duckett

ここにいる多くの人は、BitConverterが専用のstructよりも優れているかどうかについて議論しているようです。 BCLソースコードに基づくと、BitConverter.GetBytes()は次のようになります。

public static unsafe byte[] GetBytes(int value)
{
    byte[] buffer = new byte[4];
    fixed (byte* bufferRef = buffer)
    {
        *((int*)bufferRef) = value;
    }
    return buffer;
}

私の観点からは、これは、このような明示的な構造体に1つの整数+4バイトの割り当てを行うよりもクリーンで高速に見えます。

[StructLayout(LayoutKind.Explicit)]
struct IntByte
{
  [FieldOffset(0)]
  public int IntVal;
  [FieldOffset(0)]
  public byte Byte0;
  [FieldOffset(1)]
  public byte Byte1;
  [FieldOffset(2)]
  public byte Byte2;
  [FieldOffset(3)]
  public byte Byte3;
}

new IntByte { IntVal = 10 } -> Byte0, Byte1, Byte2, Byte3.
4
fiinix
class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        unsafe{
            byte[] byteArray = new byte[4];
            for(int i = 0; i != int.MaxValue; ++i)
            {
            fixed(byte* asByte = byteArray)
               *((int*)asByte) = 43;
               }
        }
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.Read();
    }
}

私のマシンでは平均約2770msですが

[StructLayout(LayoutKind.Explicit)]
struct Switcher
{
  [FieldOffset(0)]
  public int intVal;
  [FieldOffset(0)]
  public byte b0;
  [FieldOffset(1)]
  public byte b1;
  [FieldOffset(2)]
  public byte b2;
  [FieldOffset(3)]
  public byte b3;
}
class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        byte[] byteArray = new byte[4];
        Switcher swi = new Switcher();
        for(int i = 0; i != int.MaxValue; ++i)
        {
          swi.intVal = 43;
          byteArray[0] = swi.b0;
          byteArray[1] = swi.b1;
          byteArray[2] = swi.b2;
          byteArray[3] = swi.b3;
        }
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.Read();
    }
}

平均約4510ms。

2
Jon Hanna

これはC#で最速の方法かもしれないと思います(バイト配列がint32付きのintストリームの4倍に初期化されています)

        private MemoryStream Convert(int[] Num, byte[] Bytes)
    {
        Buffer.BlockCopy(Num, 0, Bytes, 0, Bytes.Length);
        MemoryStream stream = new MemoryStream(Bytes);
        return stream;
    }
1
JGU

ユニオンは、整数をバイトに分割する最速の方法です。以下は、各バイトが合計され、合計が出力されるため、C#オプティマイザーがバイト分割操作を最適化できない完全なプログラムです。

私のラップトップのタイミングはnionの場合は419ミリ秒およびBitConverterの場合は461ミリ秒です。ただし、速度の向上ははるかに大きくなります。

このメソッドは、オープンソースの高性能アルゴリズム HPCsharp ライブラリで使用されます。ここで、Unionメソッドは基数ソートのパフォーマンスを向上させます。

ユニオンは、ビット単位のマスキングやビットシフトを実行せず、4バイト整数から適切なバイトを読み取るだけなので高速です。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace SplitIntIntoBytes
{
    [StructLayout(LayoutKind.Explicit)]
    struct FooUnion
    {
        [FieldOffset(0)]
        public byte byte0;
        [FieldOffset(1)]
        public byte byte1;
        [FieldOffset(2)]
        public byte byte2;
        [FieldOffset(3)]
        public byte byte3;

        [FieldOffset(0)]
        public int integer;
    }
    class Program
    {
        static void Main(string[] args)
        {
            testUnion();
            testBitConverter();

            Stopwatch Timer = new Stopwatch();

            Timer.Start();
            int sumTestUnion = testUnion();
            Timer.Stop();

            Console.WriteLine("time of Union:        " + Timer.ElapsedTicks + " milliseconds,  sum: " + sumTestUnion);

            Timer.Restart();
            int sumBitConverter = testBitConverter();
            Timer.Stop();

            Console.WriteLine("time of BitConverter: " + Timer.ElapsedTicks + " milliseconds,  sum: " + sumBitConverter);
            Console.ReadKey();
        }

        static int testBitConverter()
        {
            byte[] UnionBytes = new byte[4];
            byte[] SumOfBytes = new byte[4];
            SumOfBytes[0] = SumOfBytes[1] = SumOfBytes[2] = SumOfBytes[3] = 0;

            for (int i = 0; i < 10000; i++)
            {
                UnionBytes = BitConverter.GetBytes(i);
                SumOfBytes[0] += UnionBytes[0];
                SumOfBytes[1] += UnionBytes[1];
                SumOfBytes[2] += UnionBytes[2];
                SumOfBytes[3] += UnionBytes[3];
            }
            return SumOfBytes[0] + SumOfBytes[1] + SumOfBytes[2] + SumOfBytes[3];
        }

        static int testUnion()
        {
            byte[] UnionBytes;
            byte[] SumOfBytes = new byte[4];
            SumOfBytes[0] = SumOfBytes[1] = SumOfBytes[2] = SumOfBytes[3] = 0;

            FooUnion union = new FooUnion();

            for (int i = 0; i < 10000; i++)
            {
                union.integer = i;
                UnionBytes = new byte[] { union.byte0, union.byte1, union.byte2, union.byte3 };
                SumOfBytes[0] += UnionBytes[0];
                SumOfBytes[1] += UnionBytes[1];
                SumOfBytes[2] += UnionBytes[2];
                SumOfBytes[3] += UnionBytes[3];
            }
            return SumOfBytes[0] + SumOfBytes[1] + SumOfBytes[2] + SumOfBytes[3];
        }
    }
}
1
DragonSpit