至少ARMv5的ARM CPU不允许随机访问非字对齐的内存地址。这里详细描述了这个问题:http://lecs.cs.ucla.edu/wiki/index.php/XScale_alignment - 一种解决方案是重写代码或首先考虑这种对齐。然而,它没有说怎么样。给定一个字节流,其中我有2个或4个字节的整数,它们在流中没有字对齐。如何以智能方式访问这些数据而不会损失太多性能?
我有一个代码片段来说明问题:
#include <stdio.h>
#include <stdlib.h>
#define BUF_LEN 17
int main( int argc, char *argv[] ) {
unsigned char buf[BUF_LEN];
int i;
unsigned short *p_short;
unsigned long *p_long;
/* fill array */
(void) printf( "filling buffer:" );
for ( i = 0; i < BUF_LEN; i++ ) {
/* buf[i] = 1 << ( i % 8 ); */
buf[i] = i;
(void) printf( " %02hhX", buf[i] );
}
(void) printf( "\n" );
/* testing with short */
(void) printf( "accessing with short:" );
for ( i = 0; i < BUF_LEN - sizeof(unsigned short); i++ ) {
p_short = (unsigned short *) &buf[i];
(void) printf( " %04hX", *p_short );
}
(void) printf( "\n" );
/* testing with long */
(void) printf( "accessing with long:" );
for ( i = 0; i < BUF_LEN - sizeof(unsigned long); i++ ) {
p_long = (unsigned long *) &buf[i];
(void) printf( " %08lX", *p_long );
}
(void) printf( "\n" );
return EXIT_SUCCESS;
}
在x86 CPU上,这是输出:
filling buffer: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
accessing with short: 0100 0201 0302 0403 0504 0605 0706 0807 0908 0A09 0B0A 0C0B 0D0C 0E0D 0F0E
accessing with long: 03020100 04030201 05040302 06050403 07060504 08070605 09080706 0A090807 0B0A0908 0C0B0A09 0D0C0B0A 0E0D0C0B 0F0E0D0C
在ATMEL AT91SAM9G20 ARMv5核心上我得到了(注意:这是该CPU的预期行为!):
filling buffer: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
accessing with short: 0100 0100 0302 0302 0504 0504 0706 0706 0908 0908 0B0A 0B0A 0D0C 0D0C 0F0E
accessing with long: 03020100 00030201 01000302 02010003 07060504 04070605 05040706 06050407 0B0A0908 080B0A09 09080B0A 0A09080B 0F0E0D0C
所以我想要或者必须在不对齐的地址访问字节流:我将如何在ARM上有效地执行此操作?
答案 0 :(得分:2)
您编写自己的打包/解包功能,它们在对齐的变量和未对齐的字节流之间进行转换。例如,
void unpack_uint32(uint8_t* unaligned_stream, uint32_t* aligned_var)
{
// copy byte-by-byte from stream to var, you can fill in the details
}
答案 1 :(得分:1)
您的示例将在任何平台上演示问题。当然简单的解决方法:
unsigned char *buf;
int i;
unsigned short *p_short;
unsigned long p_long[BUF_LEN>>2];
如果你不能用更好的对齐来组织数据(更多的字节有时候可以提供更好的性能)那么就做那么明显并将所有内容都解决为32位并从那里删除部分,优化器会处理很多一个字中的短路和字节(实际上包括结构中的字节和短路,无论是结构还是从存储器中挑选的字节,都可能更昂贵,因为会有额外的指令,而不是你把所有东西作为单词传递,你必须做的你的系统工程)。
提取未对齐单词的示例。 (当然必须管理你的结尾)
a = (lptr[offset]<<16)|(lptr[offset+1]>>16);
从armv4到现在的所有手臂核都允许不对齐访问,大多数情况下默认情况下打开例外但您可以将其关闭。现在较旧的那些在单词内旋转,但是如果我没有弄错的话,其他人可以抓住其他字节通道。
进行系统工程,进行性能分析,并确定将所有内容作为单词移动是更快还是更慢。实际移动数据会产生一些开销,但如果一切都对齐,双方的代码运行速度会快得多。您是否可以减少数倍X数据移动,以便在生成和接收数据时提高2倍到4倍?
答案 2 :(得分:0)
此函数始终使用对齐的32位访问:
uint32_t fetch_unaligned_uint32 (uint8_t *unaligned_stream)
{
switch (((uint32_t )unaligned_stream) & 3u)
{
case 3u:
return ((*(uint32_t *)unaligned_stream[-3]) << 24)
| ((*(uint32_t *)unaligned_stream[ 1]) & 0xffffffu);
case 2u:
return ((*(uint32_t *)unaligned_stream[-2]) << 16)
| ((*(uint32_t *)unaligned_stream[ 2]) & 0x00ffffu);
case 1u:
return ((*(uint32_t *)unaligned_stream[-1]) << 8)
| ((*(uint32_t *)unaligned_stream[ 3]) & 0x0000ffu);
case 0u:
default:
return *(uint32_t *)unaligned_stream;
}
}
它可能比分别读取和移动所有4个字节更快。