原创 设计 arm 中的 unaligned structure 的 access function(二)

2010-5-11 16:30 3939 8 13 分类: 消费电子

<续上>

对上述讨论的简单总结:

(1) arm coding , 定义 structure 是一件让人份外要小心的事情. 原因就是 arm 要求被访问的 data 都是 aligned .

(2) 我们定义我们的 structure, 需要妥善安排每个成员的 size 和次序, 调整它们为 aligned. 不给 compiler 加入 pad bytes 的机会. 我们能够设想, 为此, 在很多情况下, 我们甚至不得不自行加入 pad member 来凑数.

(3) 如果我们设计出的 structure aligned, 我们可以自由进行 access. 编译器产生的 code 最小, access 耗费时间最少.

(4) 可是如果我们厌烦在 structure 定义时, 还要遵守这种 align 的费心工作(往往这是不切实际的), 我们可以简单使用 #pragma pack(1) 来定义我们的 structure. 这样的被定义的 structure 被称之为 packed structure type. 比如:

#pragma pack(1)

struct {

short s;

char c;

long l;

char c2;

} s;

       形如该定义, 我们将使 s 以真实的size arm 访问. 而尽管存在没有对齐4 member l, c2, 编译器仍然不会给我们的 s 结构体中, 自行加入 pad bytes.

       (5) 如果我们使用 #pragma pack(1), align 1来对齐, 那么我们不必为安排和设计每个 member 而费心. 但是我们在这种定义下, 进行 access unaligned member. 编译器会为我们加上富余的代码, 这是为了找到正确的位置. 注意到, size speed, 我们同时将遭受损失.

       (6) 对于使用 #pragma pack(1) 定义的 structure, 我们特别要注意到, 使用指针去access unaligned member. 可能会导致错误的结果. 紧盯着 compiler warning, 我们不难发现什么时候, 我们将犯这个错误.

 

设计 arm 中的 unligned structure access function:

原则:

为了使用真实的 size, 我们仍使用 #pragma pack(1) 进行定义.

为了避免总结中提到的两个弊端: access unaligned member code 增加以及 speed 减慢, 以及可能使用指针access unaligned member 的错误, 我们设计我们自己的 access function. 而不是简单使用 stuctA.memberB, 或是 structA_pointer->memberB 来进行访问:

      

实现:

我们用一个 unaligned sample 来进行说明:

//// test

// store with real space

#pragma pack(1)

typedef struct

{

        U8 data1;

        U16 data2;

        U32 data3;

} TEST_STRUCT;

 

TEST_STRUCT test_config = {

        .data1 = 0,

        .data2 = 0,

        .data3 = 0

};

 

#define DATA1_OFFSET    0

#define DATA2_OFFSET    1

#define DATA3_OFFSET    3

 

U32 test_config_read_u32(U32 offset)

{

        return *(U32*)((U8*)&test_config + offset);

}

 

void test_config_write_u32(U32 data, U32 offset)

{

        *(U32*)((U8*)&test_config + offset) = data;

}

 

void test(void)

{

        U32 data = test_config.data3;

        uart0_putnum(data, 10);    // output   

        test_config.data3 = data+1;

        data = test_config.data3;

        uart0_putnum(data, 10);    // output   

}

 

void test1(void)

{

        U32 data = test_config_read_u32(DATA3_OFFSET);

        uart0_putnum(data, 10);    // output 

        test_config_write_u32(data+1, DATA3_OFFSET);       

        data = test_config_read_u32(DATA3_OFFSET);

        uart0_putnum(data, 10);    // output 

}

 

在本例中, 我们定义了一个 unaligned data3. 同时我们自行完成了 read/write access function. test() test1() 的简单比较中, code size 相差了多少呢? – 整整 52 bytes~ speed上的改变, 限于紧张工作时间, 我们没有写代码去比较.

       因此, 在定义 real space align 1 structure , 我们可以自行完成, access 函数, 从而避免了 compiler unaligned data access, 带来的富余的 code, 以及speed 的损失, 同时也避免了, 指向 unaligned data 的指针, 可能带来的执行错误.

 

       (3) 进一步比较, 为了将我们自行定义的 access function() aligned structure 进行比较, 我们将 sample structure 定义修改为:

#pragma pack(1)

typedef struct

{

        U16 data1;             // we set U16 data type for aligned

        U16 data2;

        U32 data3;

} TEST_STRUCT;

 

这时, 我们观察到 data3 aligned . 再次测试 test(). 非常意外的, 在同等编译条件下, 我们获得了不多不少 52bytes code size 的节省. 等同我们自行完成 access function unaligned 的实现效果.

       这给我们带了一个稍微更深远的猜测: 是否对于 aligned structure, 编译器完成 read/write 的技巧, 与我们从首地址开始操作 offset 的模式, 是一致的呢?

 

结论:

packed structure(align 1) 的情况下, 我们使用自行定义的 access function, 将带来较多的 size 的节省, 对于大量使用 packed structure project, 节省出富余的 size 将是惊人的看看我们的sample 的数据. 此外还带来 access speed 增加. 因此最终可达到系统优化之目的.

Sample 中我们实现了 U32 read/write. 我相信很容易的, 同行们拓展到 U8, U16, str 都是不太困难的JOB.

 

P.S.
在 review 我们的 sample 的过程中, 我们发现一个bug, 仍然源于 arm 的 align 机制, 单纯使用 U32* 取数据时, 也是按照4来做严格对准, 这样和真实的 real space 可能不一致, 修改我们的 sample 中的 access function 如下:

////

// app_config_read_u8
U8 app_config_read_u8(U32 offset)
{
        return *(U8*)((U8*)&flash_app_config + offset);
}
// app_config_write_u8
void app_config_write_u8(U8 data, U32 offset)
{
        *(U8*)((U8*)&flash_app_config + offset) = data;
}

// app_config_read_u32
U32 app_config_read_u32(U32 offset)
{
        U32 sum = 0;
        for(U32 i=0; i<4; i++)
            sum += app_config_read_u8(offset+i)<<(i<<3);
       
        return sum;
}
// app_config_write_u32
void app_config_write_u32(U32 data, U32 offset)
{
        for(U32 i=0; i<4; i++) {
            app_config_write_u8(data, offset+i);
            data >>= 8;
        }
}
////

比较而言, 代码的 size 会有所增加, 比较原有 sample 中的代码. 但是修改后的 code 保证了正确性, 并且在整个 project 中, 仍然无损于我们达到优化系统性能的, 这个中心目的.

Allen

allen_zhan@163.com
GV-TECH
2010.5.10 发表于<电子工程专辑>

设计 arm 中的 unaligned structure 的 access function(一)
设计 arm 中的 unaligned structure 的 access function(二)


PARTNER CONTENT

文章评论5条评论)

登录后参与讨论

1989tie_959541171 2013-2-26 13:32

谢谢Allen兄的回复,从这里memcpy的用法上看确实不用考虑内存对齐access的问题。这里的access function针对于主要的structure做优化,效果会很好。这里能够这样做的structure也是我们最关心的。 之所以会想到,memcpy access align memory的问题,因为这样更容易做到块copy,copy的效率也更高。

allen_zhan_752827529 2013-2-22 18:39

hello catch2000 兄, 我这里说的 memcpy 的方式, 仍然是延续这篇文章的重要观点, 用自行 offset 的方式, 自行构造 access function 去访问每个 unaligned member, 而并非意指其他. 用上 memcpy 是使我们的 access function 看起来可读性更强, 比方说, 直接读取 32-bit 的无符号整形member, 我们可以写作: U32 basic_read_u32(U32 offset) { U32 data = 0; memcpy((U8*)&data, basic_str_ptr(offset), sizeof(U32)); return data; } 这里我们没有去考虑 memcpy 如何被 compiler 去优化实现. 但是只要这个 compiler 还是 C compiler. 那么我们完全知道, 这个 access function 将忠实执行我们力图达到的准确访问, 在该 struction 中定义的任何 u32 的 member, 而不用关心它是否是 aligned. 所有的技巧和秘诀只不过是记录好每个 member 的 offset 而已, 难道这不是一件很easy 的事儿吗... - Allen

1989tie_959541171 2013-2-21 20:43

博主,分析的很深入。有时间也测试下看看。 对于memcpy在复制时,是按byte进行,所以可以直接复制出来,对不? 不过有时候memcpy的运行时库,由于优化的缘故,可能不是按byte的复制。这个时候,是怎么解决呢?

allen_zhan_752827529 2010-5-13 08:43

在最终的工作中, 我们使用 memcpy 简单更高效完成 access function - 一个提示.

用户1277994 2010-5-10 21:22

多谢allen!
相关推荐阅读
allen_zhan 2023-02-27 19:08
对"三极管"译名由来的探讨
想讨论一个有意思的话题:今天中国大陆的电子业界, 为何将 BJT 称呼为 "三极管"? 或因其象形, 前辈自行进行随意的不严谨定义么? 带着疑问我们做了一下延伸查阅, 或得出这样的结论, 即中译名"三...
allen_zhan 2023-02-19 18:15
对知乎提问"为何三极管的一个PN结工作在反偏"的回复
将这个回复, 也发表在博文中, 作为自己的一个学习笔记叭.知乎问题: "三极管里面的PN结相当于二极管,为什么里面PN结加反向电压也能导通?"我的回复:首先, 二极管的"反向"概念, 容易给初学者某种...
allen_zhan 2023-02-18 10:17
从肖特基二极管到PN结与三极管
最近数个工作日的兴趣是回顾电子基础器件的发明/发展历史, 期待夯实技术基础的底蕴. 在学习与搜索资料的过程中, 顺便对知乎的一个同学的基础问题, 进行了回复. 不小心回复一下就成了千字文, 觉得挺有趣...
allen_zhan 2023-01-28 11:53
微功率 ISM 频率探讨相关文档组总结
不知不觉, 自开启关于微功率频率的话题起, 即从第一份文章写就到今天总结之日, 已经接近 10 个工作日左右. 早先的想法是对工程界未来的微功率设备相关项目, 从项目规划开始, 对选择系统, 频率, ...
allen_zhan 2023-01-27 22:50
关于 LoRa 应用场景的讨论
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. "第52号文" 对 470MHz 的约束引自 如下:(四)民用计量仪表限在建筑楼宇、住宅小区及村庄等小范围内组网应用,任意时刻限...
allen_zhan 2023-01-25 13:24
ISM 频段中 2.4G 与 5.8GHz 设备的使用与限制
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. ISM 频段定义中的 2.4G 与 5.8GHz正如同 文中确定的, 2.4G, 5.8GHz 属于中国大陆 ISM 频段的定义...
我要评论
5
8
关闭 站长推荐上一条 /3 下一条