2012-01-11 2 views
16

Что такое быстрый способ конвертировать int в 4 байта на C#?Самый быстрый способ конвертировать 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 на пару тиков.

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

+1

Под «самым быстрым» вы подразумеваете a) наименьшее количество кода или b) наилучшую производительность? –

+0

Лучшая производительность – MichaelT

+2

«Самый быстрый» зависит от того, где вы хотите, чтобы эти байты были отправлены. –

ответ

14

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

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

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

+0

;) На самом деле это небезопасно - и это не нужно;) Действительно. Решение Anotehr safe (!) Имеет такую ​​же скорость. – TomTom

+6

Да, поэтому используется ключевое слово * unsafe *. Который в значительной степени доступен, чтобы сделать именно это, написать быстрый код. Запрос OP был для * fast *, not * similar *. –

+0

Интересно, что не меняет того факта, что для этой конкретной проблемы существует такое же быстрое и безопасное решение. Эрго небезопасное решение хуже. Это другое для других проблем, но этот конкретный человек получил бы второе место в обзоре от меня за небезопасный код - пройденный, пожалуйста, переписывайте. – TomTom

14

Что такое быстрый способ преобразования int в 4 байта на C#?

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

int i = 123; 
byte[] buffer = BitConverter.GetBytes(i); 
+0

Фактически фактически неправильно. – TomTom

+0

@TomTom, нужно разработать? –

+0

@TomTom: Небезопасно было бы быстрее или что-то тогда? –

6

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

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

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

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


EDIT: Испытание ниже не является окончательным из-за компилятор оптимизации.


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

      } 
     } 
    } 
} 
+0

Слишком медленно, извините. Существуют более быстрые методы, не связанные с каким-либо методом. – TomTom

+1

Смотрите мое редактирование для теста скорости, не уверен, что я допустил ошибку, но не думаю, что так. Он показывает, что BitConverter работает быстрее, если вам нужен массив (хотя и не очень много). –

+5

Собирают ли я запустить это сам? Бу!! ;-P –

8

Самый быстрый способ состоит из структуры, содержащей 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; 

} 
+0

Мне нравится умение вашего решения – MichaelT

+0

Мне тоже ... Я просто хочу, чтобы его было легче читать (ответ, а не код), и с ним были некоторые ориентиры. +1 в любом случае –

+0

Чистый, простой, понятный. Я не мог заботиться о производительности, когда код является доказательством пули. ++ – RubberDuck

2
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.

+0

Fake test - исправляет новые условия. Первоначально не было слова из массива, и вы заменяете указатель arithmethid на безопасные массивы, протестированные с помощью закодированного кода, - поэтому тест искажается по отношению к границе массива, проверяя отрицательно на структурное решение. – TomTom

+0

@TomTom Конечно, это поддельный тест, все тесты являются поддельными, за исключением того, что видят, как он работает в реальной жизни. Ни одно из них не оптимизируется для одного из них, поэтому оно служит. Мы могли бы заменить копию массива, поместив его в четыре отдельных байта, и по-прежнему важно, чтобы на самом деле получение байтов где-то добавляется к структурному подходу. Это хороший обход для случаев, когда вы не можете использовать 'unsafe', и это происходит примерно на полмиллиарда циклов в секунду, так как не будет никаких шансов. –

8

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

+0

Сможет ли это сдвинуть скорость? – Karussell

+0

@ Karussell Возможно, вы были правы, я почему-то не думал об этом, но вы можете проверить его и поделиться результатами здесь :) –

1

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

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

Как и многие здесь, кажется, спорить о том, если 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. 
Смежные вопросы