<续上>
对上述讨论的简单总结:
(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(二)
1989tie_959541171 2013-2-26 13:32
allen_zhan_752827529 2013-2-22 18:39
1989tie_959541171 2013-2-21 20:43
allen_zhan_752827529 2010-5-13 08:43
用户1277994 2010-5-10 21:22