Быстрое литье в C# с помощью BitConverter, может ли это быть быстрее?

в нашем приложении у нас очень большой байт-массив, и мы должны преобразовать эти байты в разные типы. В настоящее время, мы используем BitConverter.ToXXXX() для этой цели. Наши тяжелые нападающие,ToInt16 и ToUInt64.

на UInt64, наша проблема в том, что поток данных фактически имеет 6 байтов данных для представления большого целого числа. Поскольку нет собственной функции для преобразования 6-байтов данных в UInt64, мы делаем:

UInt64 value = BitConverter.ToUInt64() & 0x0000ffffffffffff;

использование ToInt16 проще, не нужно делать любые манипуляции.

мы делаем так много из этих 2 операций, что я хотел спросить сообщество SO, есть ли более быстрый способ сделать эти преобразования. Прямо сейчас, приблизительно 20% всех наших циклов процессора потребляется этими двумя функциями.

4 ответов


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

        byte[] arr = { 1, 2, 3, 4, 5, 6, 7, 8 ,9,10,11,12,13,14,15,16};

        fixed (byte* a2rr = &arr[0])
        {

            UInt64* uint64ptr = (UInt64*) a2rr;
            Console.WriteLine("The value is {0:X2}", (*uint64ptr & 0x0000FFFFFFFFFFFF));
            uint64ptr = (UInt64*) ((byte*) uint64ptr+6);
            Console.WriteLine("The value is {0:X2}", (*uint64ptr & 0x0000FFFFFFFFFFFF));
        }

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


можно использовать System.Buffer класс для копирования целого массива в другой массив другого типа в качестве быстрого, 'block copy' работы:

метод BlockCopy обращается к байтам в массиве параметров src, используя смещения в память, а не программируя конструкции, такие как индексы или верхние и нижние границы массива.

типы массивов должны быть "примитивных" типов, они должны выравниваться, и операция копирования чувствительный к эндиану. В вашем случае 6-байтовых целых чисел он не может выровняться ни с одним из "примитивных" типов .NET, если вы не можете получить исходный массив с двумя байтами заполнения для каждого из шести, который затем выровняется по Int64. Но этот метод будет работать для массивов Int16, который может ускорить некоторые операции.


почему бы и нет:

UInt16 valLow = BitConverter.ToUInt16();
UInt64 valHigh = (UInt64)BitConverter.ToUInt32();
UInt64 Value = (valHigh << 16) | valLow;

вы можете сделать это одним оператором, хотя компилятор JIT, вероятно, сделает это для вас автоматически.

это помешает вам прочитать те дополнительные два байта, которые вы в конечном итоге выбрасываете.

Если это не уменьшает CPU, то вы, вероятно, захотите написать свой собственный конвертер, который считывает байты непосредственно из буфера. Вы можете использовать индексирование массива или, если считаете это необходимым, небезопасный код с указатели.

отметим, что, как комментатор указал, если вы используете какой-либо из этих предложений, то либо вы ограничены определенной "обратным порядком байтов-Несс", или вы будете иметь, чтобы написать код, чтобы обнаружить маленький/большой endian и реагировать соответственно. Пример кода, который я показал выше, работает для little endian (x86).


смотрите мой ответ на аналогичный вопрос здесь. Это та же небезопасная манипуляция памятью, что и в ответе Джимми, но более "дружелюбная" для потребителей. Это позволит вам просмотреть byte массив as UInt64 массив.