Быстрое приведение в C # с использованием BitConverter, это может быть быстрее? - PullRequest
15 голосов
/ 07 февраля 2011

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

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

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

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

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

Ответы [ 4 ]

7 голосов
/ 07 февраля 2011

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

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

4 голосов
/ 07 февраля 2011

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

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

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

2 голосов
/ 07 февраля 2011

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

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

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

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

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

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

0 голосов
/ 10 мая 2012

Смотрите мой ответ на похожий вопрос здесь . Это та же небезопасная манипуляция с памятью, что и в ответе Джимми, но более «дружелюбная» для потребителей. Это позволит вам просматривать ваш массив byte как массив UInt64.

...