C: Memcpy против Shifting: что эффективнее? - PullRequest
4 голосов
/ 06 февраля 2012

У меня есть байтовый массив, содержащий 16- и 32-битные выборки данных, и для приведения их к Int16 и Int32 в настоящее время я просто делаю memcpy с 2 (или 4) байтами.

Поскольку memcpy, вероятно, не оптимизирован для длин всего двух байтов, мне было интересно, будет ли эффективнее преобразовывать байты с использованием целочисленной арифметики (или объединения) в Int32.

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

Ответы [ 2 ]

4 голосов
/ 06 февраля 2012

Я бы сказал, что memcpy не способ сделать это.Однако, поиск best сильно зависит от того, как ваши данные хранятся в памяти.

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

Самое общее решение - прочитать данные побайтно и арифметически объединить результат.Например:

uint16_t res = (  (((uint16_t)char_array[high]) << 8)
                | char_array[low]);

Выражение в 32-битном случае немного сложнее, поскольку у вас есть больше альтернатив.Возможно, вы захотите проверить вывод ассемблера, который лучше.

Alt 1: построить Париж и объединить их:

uint16_t low16 = ... as example above ...;
uint16_t high16 = ... as example above ...;
uint32_t res = (  (((uint32_t)high16) << 16)
                | low16);

Alt 2: сдвиг в 8 битов за раз:

uint32_t res = char_array[i0];
res = (res << 8) | char_array[i1];
res = (res << 8) | char_array[i2];
res = (res << 8) | char_array[i3];

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

Возможен следующий вид решений, если 1) порядок байтов (порядок байтов)) устройства соответствуют порядку, в котором байты хранятся в массиве, и 2) массив, как известно, размещается по выровненному адресу памяти.Последний случай зависит от машины, но вы в безопасности, если массив char, представляющий 16-битный массив, начинается с четного адреса, а в 32-битном случае он должен начинаться с адреса, делимого на четыре.В этом случае вы можете просто прочитать адрес после некоторых уловок указателя:

uint16_t res = *(uint16_t *)&char_array[xxx];

Где xxx - индекс массива, соответствующий первому байту в памяти.Обратите внимание, что это может не совпадать с индексом наименьшего значения.

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

В любом случае, оба онинамного быстрее, чем ваше memcpy решение.

2 голосов
/ 06 февраля 2012

memcpy недопустимо для «сдвига» (перемещение данных на смещение, которое короче его длины в том же массиве); попытка использовать его для таких действий вызывает очень опасное неопределенное поведение. Смотри http://lwn.net/Articles/414467/

Вы должны использовать memmove или свой собственный цикл переключения. Для размеров выше около 64 байтов я бы ожидал, что memmove будет намного быстрее. Для очень коротких смен ваш собственный цикл может выиграть. Обратите внимание, что memmove имеет больше служебных данных, чем memcpy, потому что он должен определить, какое направление копирования является безопасным. Ваш собственный цикл уже (предположительно) знает, какое направление является безопасным, поэтому он может избежать дополнительной проверки во время выполнения.

...