Самый быстрый способ конвертировать int в 4 байта в C # - PullRequest
18 голосов
/ 12 января 2012

Какой самый быстрый способ преобразовать int в 4 байта в C #?

Самый быстрый, так как во время выполнения, а не во время разработки.

Мое собственное решение - этот код:

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

Сейчас я вижу, что мое решение превосходит как struct, так и BitConverter по паре тиков.

Я думаю, что небезопасный, вероятно, самый быстрый вариант, и принимаю это как ответ, но я бы предпочелиспользовать управляемую опцию.

Ответы [ 9 ]

16 голосов
/ 12 января 2012

Какой самый быстрый способ преобразовать int в 4 байта в C #?

Используя BitConverter и это перегрузка GetBytes , которая требует32-разрядное целое число:

int i = 123;
byte[] buffer = BitConverter.GetBytes(i);
15 голосов
/ 12 января 2012

Байт * приведение с использованием небезопасного кода является самым быстрым:

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

Это то, что BitConverter также делает, этот код позволяет избежать затрат на создание массива.

10 голосов
/ 08 февраля 2013

Я провел исследование времени, необходимого для сериализации базового типа в байтовый массив. Я сделал это для случая, когда у вас уже есть массив и смещение, куда вы хотите поместить свои данные. Я думаю, что это действительно важный случай по сравнению с теоретическим получить массив из 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     209    233    220    212    208     228
3     63     24     13     8      24      44
4     72     29     14          

Как вы можете видеть, небезопасный способ намного быстрее для длинных и двойных (неподписанные версии примерно такие же, как их подписанные версии, поэтому их нет в таблице). Для 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. Тесты выполнялись в среде x64 с кодом, скомпилированным для любого процессора (который был x64 при запуске) в режиме выпуска.

8 голосов
/ 12 января 2012

Самый быстрый способ - это структура, содержащая 4 байта.

  • В определенной компоновке (в позиции байтов 0, 1, 2, 3
  • и int32, который начинается впозиция 0.
  • Введите 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;

}
6 голосов
/ 12 января 2012

Обратите внимание, что BitConverter может быть не самым быстрым, как показывает тест, приведенный ниже.

Используйте класс BitConverter, в частности метод GetBytes, который принимает параметр Int32:

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

Вы можете использовать BitConverter.IsLittlEndian для определения порядка байтов на основе архитектуры ЦП.


РЕДАКТИРОВАТЬ: Тест ниже не является окончательным из-за оптимизации компилятора.


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

            }
        }
    }
}
4 голосов
/ 24 февраля 2015

Как многие здесь спорят, если 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.
2 голосов
/ 12 января 2012
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();
    }
}

В среднем около 2770 мс на моей машине, а

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

В среднем около 4510 мс.

1 голос
/ 06 января 2019

Объединение - это самый быстрый способ разбить целое число на байты. Ниже приведена полная программа, в которой оптимизатор C # не может оптимизировать операцию разделения байтов, поскольку каждый байт суммируется, а сумма распечатывается.

Время на моем ноутбуке составляет 419 миллисекунд для Union и 461 миллисекунд для BitConverter . Однако прирост скорости намного больше.

Этот метод используется в высокопроизводительных алгоритмах с открытым исходным кодом. Библиотека HPCsharp , где метод Union обеспечивает хорошее повышение производительности для сортировки по Radix.

Объединение выполняется быстрее, поскольку оно не выполняет битовую маскировку и не выполняет сдвиг битов, а просто считывает надлежащий байт из 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 голос
/ 13 сентября 2013

Я думаю, что это может быть самый быстрый способ в C # .. (с байтовым массивом, инициализированным в 4 раза потоком int, с int32

        private MemoryStream Convert(int[] Num, byte[] Bytes)
    {
        Buffer.BlockCopy(Num, 0, Bytes, 0, Bytes.Length);
        MemoryStream stream = new MemoryStream(Bytes);
        return stream;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...