x86的代码执行此操作(n只能是1到4,在编译时未知):
static const uint32_t wordmask[] = {
0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu
};
static inline uint32_t get_unaligned_le_x86(const void *p, uint32_t n) {
uint32_t ret = *(const uint32_t *)p & wordmask[n];
return ret;
}
对于没有未对齐的32位小端序加载的架构,我有两种变体:
static uint32_t get_unaligned_le_v1(const void *p, uint32_t n) {
const uint8_t *b = (const uint8_t *)p;
uint32_t ret;
ret = b[0];
if (n > 1) {
ret |= b[1] << 8;
if (n > 2) {
ret |= b[2] << 16;
if (n > 3) {
ret |= b[3] << 24;
}
}
}
return ret;
}
static uint32_t get_unaligned_le_v2(const void *p, uint32_t n) {
const uint8_t *b = (const uint8_t *)p;
uint32_t ret = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
ret &= wordmask[n];
return ret;
}
在读取硬件上会更好(我使用qemu进行开发),你能建议更快的替代方案吗?如果速度快得多,我就可以使用汇编了。
答案 0 :(得分:4)
ARM上的条件执行是提高性能的最佳选择。 ARM上的表查找(掩码)肯定会慢一些。这是我的ARMv5实现:
// When called from C, r0 = first parameter, r1 = second parameter
// r0-r3 and r12 can get trashed by C functions
unaligned_read:
ldrb r2,[r0],#1 ; byte 0 is always read (n=1..4)
cmp r1,#2
ldrgeb r3,[r0],#1 ; byte 1, n >= 2
ldrgtb r12,[r0],#1 ; byte 2, n > 2
orrge r2,r2,r3,LSL #8
orrgt r2,r2,r12,LSL #16
cmp r1,#4
ldreqb r3,[r0],#1 ; byte 3, n == 4
movne r0,r2 ; recoup wasted cycle
orreq r0,r2,r3,LSL #24
mov pc,lr ; or "bx lr" for thumb compatibility
更新:将ldreqb修复为ldrgeb
更新2 :通过在最后一个ldr / orr之间插入指令来削减另一个周期
答案 1 :(得分:0)
你需要测试一下。这很难说,因为它不仅取决于处理器架构,还取决于编译器,编译标志和目标系统。
这是另一个可以消除分支和表查找(未经测试的代码)的想法/技巧:
char mask1 = -(n>1); // 0 if n<=1, 0xFF otherwise
char mask2 = -(n>2);
char mask3 = -(n>3);
ret = b[0];
ret |= (b[1] & mask1) << 8;
ret |= (b[2] & mask2) << 16;
ret |= (b[3] & mask3) << 24;
请注意,这是您的第二个功能,可以读取输入的结尾,这可能是也可能不是问题。
我生成了这个代码,看起来不那么糟糕(15个指令,没有分支,没有表查找):
cmp r1, #2
ldrb r2, [r0, #2] @ zero_extendqisi2
ldrb r4, [r0, #1] @ zero_extendqisi2
movls r2, #0
cmp r1, #1
ldrb ip, [r0, #0] @ zero_extendqisi2
movls r4, #0
mov r3, r2, asl #16
ldrb r2, [r0, #3] @ zero_extendqisi2
cmp r1, #3
orr r0, r3, r4, asl #8
orr r3, r0, ip
movhi r1, r2
movls r1, #0
orr r0, r3, r1, asl #24
我要尝试的另一件事是重写你的第二个函数:
if (n > 1) {
ret |= b[1] << 8;
}
if (n > 2) {
ret |= b[2] << 16;
}
if (n > 3) {
ret |= b[3] << 24;
}
由于编译器可以更好地使用conditional executio n,这比条件分支更快。
如果它的运行速度非常快,我会考虑在ARM程序集中编写它。