我有一个包含16&的字节数组。 32位数据样本,并将它们转换为Int16和Int32我目前只做一个2(或4)字节的memcpy。
因为memcpy可能没有针对只有两个字节的长度进行优化,所以我想知道使用整数算术(或联合)将字节转换为Int32是否更有效。
我想知道调用memcpy与位移的效率是多少,因为代码在嵌入式平台上运行。
答案 0 :(得分:3)
我会说memcpy
不是这样做的方法。但是,找到最佳方式很大程度上取决于数据在内存中的存储方式。
首先,您不想获取目标变量的地址。如果它是局部变量,您将强制它到堆栈而不是让编译器选择将它放在处理器寄存器中。仅此一点可能非常昂贵。
最通用的解决方案是逐字节读取数据并对结果进行算术组合。例如:
uint16_t res = ( (((uint16_t)char_array[high]) << 8)
| char_array[low]);
32位情况下的表达式有点复杂,因为您有更多选择。您可能想要检查最佳的汇编程序输出。
替补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)已知数组被放置在对齐的存储器地址上。后一种情况取决于机器,但是如果表示16位数组的字符数组在偶数地址上开始则是安全的,而在32位情况下,它应该从可分为4的地址开始。在这种情况下,你可以在一些指针技巧之后简单地读取地址:
uint16_t res = *(uint16_t *)&char_array[xxx];
其中xxx
是与内存中第一个字节对应的数组索引。请注意,这可能与最低值的索引不同。
我强烈建议使用第一类解决方案,因为它是endianess-neutral。
无论如何,它们都比你的memcpy
解决方案更快。
答案 1 :(得分:2)
memcpy
对于“移位”无效(在同一数组中移动数据的偏移量小于其长度);试图使用它来调用非常危险的未定义行为。见http://lwn.net/Articles/414467/
您必须使用memmove
或您自己的换档循环。对于大约64字节以上的大小,我希望memmove
要快得多。对于极短暂的班次,你自己的循环可能会赢。请注意,memmove
的开销高于memcpy
,因为它必须确定哪个复制方向是安全的。你自己的循环已经知道(大概)哪个方向是安全的,所以它可以避免额外的运行时检查。