编译优化之 - 结构数据布局优化入门

前言

  结构数据布局优化(Structure Data Layout Optimizations)是比较成熟也是使用广泛的编译器优化技术,旨在提高数据局部性,减少cache miss。常用的structure optimizations有:结构体拆分(structure spliting)、结构体剥离(structure peeling)、域重组(structure reordering)。

1.结构体拆分

  结构体拆分是将结构体中冷的域(不常使用的字段)和热的域(经常访问的字段)拆开,分成多个子结构体,并在原结构体中增加指针域和其它子结构体相连,拆分之后的组件也将分配给寄存器,加快访问速度。示例如下:

typedef struct Node
{
    int a;
    char ch;
    double d;
    long l;
    int *v;
}node;
// 内存分配
int size = 10;
node *my_node = (node *)calloc(size, sizeof(node));

原结构体拆分后:

typedef struct New_node
{
    node_1 *node_hot;
    node_2 *node_cold;
}new_node;
typedef struct Node_1
{
    int a;
    char ch;
    double d;
}node_1;
typedef struct Node_2
{
    long l;
    int *v;
}node_2;
// 内存分配
node_1 *arr_1= (node_1 *)calloc(size, sizeof(node_1));
node_2 *arr_2= (node_2 *)calloc(size, sizeof(node_2));
//关联他们
for (i=0; i < N; i++)
{
    my_node[i]. node_hot = &arr_1[i];
    my_node[i]. node_cold = &arr_2[i];
}

进行spliting之后的内存布局:

内存布局.png

  通常这些子结构体还可以递归拆分,采用同样的指针方式与父结构体相连。这种拆分一方面能将较大的结构体中热的域拆分成新的结构体,提高了缓存局部性,另一方面是这种转换引入了指针间接寻址,增加了结构体开销,这种优化并不一定有利,因此,需要一种良好的决策算法来有效控制此转换。


2.结构体剥离

  结构体剥离是一种将原结构体中的域剥离成多个新的结构体的优化,我们将原始结构剥离到单独的结构中,以便结构重组和字段的内存连续紧密分配,同时将对象指针改为整数索引,通过数组索引访问的方式替换指针之间的多层间接寻址。示例如下:

typedef struct Acs
{
    int a;
    int b;
    acs *next;
    acs *acs_p;
}acs;

// 内存分配
acs *arr= (acs *)calloc(size, sizeof(acs));
arcs *tmp_node = arr;
while(tmp_node)
{
    tmp_node->a = 111;
    tmp_node=tmp_node->next;
}

原结构体剥离后:

typedef struct Acs_sub1
{
    unsigned int next_index;
}acs_sub1;
typedef struct Acs_sub2
{
    int a;
    int b;
    unsigned int acs_pi;
}acs_sub2;

// 内存分配
int measu = sizeof(acs_sub1) + sizeof(acs_sub2);
acs *arr = (acs *)calloc(size, measu);
acs_sub1 *ac_1 = (acs_sub1 *)arr;
acs_sub2 *ac_2 = (acs_sub2 *)(ac_1 + size);
int tmp=0;
while(tmp!=-1)
{
    ac_2[tmp].a = 111;
    tmp = ac_1[tmp].next_index;
}

或者以下这种情况:

struct MyStruct
{
    double value1;
    double value2;
    int i;
};
MyStruct *t;

//
struct MyStruct2
{
    double value2;
    int i;
};
double *d1;
MyStruct2 *s2;

  换句话说就是:结构剥离转换是结构拆分的一种特殊情况,其中不需要引入指针。当一个变量失效时(以后将不使用其值)时,已被分配给它的寄存器将被重用。


3.域重组

  域重组是通过修改结构体中域的顺序来改变内存的对齐方式,以提高数据的亲和性。在大型的结构体中,根据某些字段的访问频率对字段顺序进行调整来提高效率,这种转换不像结构体拆分和剥离那样激进,但这种转换也使用在结构体拆分和剥离中。例如有如下结构体和使用代码:

typedef struct T
{
    int a;
    int b;
    int c;
}t;
val = &t.a;  ++val;  使用*p 
//假如结构体t,有这样的使用代码,当对结构体t进行reorder之后会怎样?
typedef struct T   
{
    int c;
    int b;
    int a;
}t;

  此时代码将会出问题,指针将会指向其它地址。如果使用t->a这样方式是没问题的,但这问题怎么解决,这就是一个“第”几个概念,采用索引的方式来实现reorder,这样不用考虑访问时的offset不固定问题,进行reorder的时候只需要对这个’id’进行操作就可以很好的解决这个问题。

typedef struct T
{
    0  ->  int a;
    1  ->  int b;
    2  ->  int c;
}t;

4.References:

  • Structure Layout Optimizations in the Open64 Compiler: Design, Implementation and Measurements
  • Struct-reorg: current status and future perspectives
  • Array Regrouping and Structure Splitting Using Whole-Program Reference Affinity
原创文章 38 获赞 13 访问量 4044

猜你喜欢

转载自blog.csdn.net/qq_36287943/article/details/103601176