gpt4 book ai didi

c - 在字对齐处理器上处理未对齐数据的最快方法?

转载 作者:太空狗 更新时间:2023-10-29 16:32:44 27 4
gpt4 key购买 nike

我正在 ARM Cortex M0 上做一个项目,它不支持未对齐(按 4 字节)访问,我正在尝试优化未对齐数据的操作速度。

我将蓝牙低功耗访问地址(48 位)作为 6 字节数组存储在一些充当数据包缓冲区的打包结构中。由于打包,BLE 地址不一定从字对齐地址开始,并且在优化我对这些地址的访问函数时遇到了一些复杂情况。

第一种也是最明显的方法是对数组中的每个字节分别进行 for 循环操作。例如,检查两个地址是否相同可以像这样完成:

uint8_t ble_adv_addr_is_equal(uint8_t* addr1, uint8_t* addr2)
{
for (uint32_t i = 0; i < 6; ++i)
{
if (addr1[i] != addr2[i])
return 0;
}
return 1;
}

我在我的项目中做了很多比较,我想看看我是否可以从这个函数中挤出更多的速度。我意识到对于对齐地址,我可以将它们转换为 uint64_t,并与应用的 48 位掩码进行比较,即

((uint64_t)&addr1[0] & 0xFFFFFFFFFFFF) == ((uint64_t)&addr2[0] & 0xFFFFFFFFFFFF)

可以对写入进行类似的操作,并且它适用于对齐版本。但是,由于我的地址并不总是字对齐(甚至半字对齐),因此我必须采取一些额外的技巧才能完成这项工作。

首先,我想到了这个编译器宏的未优化噩梦:

#define ADDR_ALIGNED(_addr) (uint64_t)(((*((uint64_t*)(((uint32_t)_addr) & ~0x03)) >> (8*(((uint32_t)_addr) & 0x03))) & 0x000000FFFFFFFF)\
| (((*((uint64_t*)(((uint32_t)_addr+4) & ~0x03))) << (32-8*(((uint32_t)_addr) & 0x03)))) & 0x00FFFF00000000)

它基本上将整个地址从前一个字对齐的内存位置开始,不管偏移量如何。例如:

    0       1       2       3
|-------|-------|-------|-------|
|.......|.......|.......|<ADDR0>|
|<ADDR1>|<ADDR2>|<ADDR3>|<ADDR4>|
|<ADDR5>|.......|.......|.......|

成为

    0       1       2       3
|-------|-------|-------|-------|
|<ADDR0>|<ADDR1>|<ADDR2>|<ADDR3>|
|<ADDR4>|<ADDR5>|.......|.......|
|.......|.......|.......|.......|

我可以安全地对两个地址进行 64 位比较,而不管它们的实际对齐方式如何:

ADDR_ALIGNED(addr1) == ADDR_ALIGNED(addr2)

整洁!但是这个操作在使用 ARM-MDK 编译时需要 71 行汇编,而在简单的 for 循环中进行比较时需要 53 行(我将忽略此处分支指令中花费的额外时间),以及 ~30展开时。此外,它不适用于写入,因为对齐仅发生在寄存器中,而不发生在内存中。再次取消对齐将需要类似的操作,而且整个方法通常看起来很糟糕。

对于这种情况,展开的 for 循环是否对每个字节单独处理真的是最快的解决方案吗?有没有人有过类似情况的经验,并想在这里分享他们的一些魔法?

最佳答案

更新

好吧,因为你的数据无论如何都没有对齐,你需要将所有数据逐字节读取到正确对齐的缓冲区中,然后进行真正快速的 64 位比较,或者,如果你不使用比较后的数据,只需以字节形式读入数据并进行 6 次比较,在这种情况下调用 memcmp() 可能是更好的选择。


对于至少 16 位对齐:


u16 *src1 = (u16 *)addr1;
u16 *src2 = (u16 *)addr2;

for (int i = 0; i < 3; ++i)
{
if (src1[i] != src2[i])
return 0;
}

return 1;

将比字节比较快两倍,并且可能是您可以合理执行的最佳操作,只要您的数据至少是 2 字节对齐的。我还希望编译器完全删除 for 循环,而只使用条件执行的 if 语句。

尝试 32 位对齐读取不会更快,除非您可以保证 source1 和 2 类似地对齐 (add1 & 0x03) == (addr2 & 0x03)。如果是这种情况,那么您可以读入一个 32 位值,然后读入一个 16 位值(或反之亦然,具体取决于起始对齐方式)并再删除 1 个比较。

由于 16 位是您的共享基础,您可以从那里开始,编译器应该生成漂亮的 ldrh 类型操作码。

关于c - 在字对齐处理器上处理未对齐数据的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29856496/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com