首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >GCC局部结构与个体变量及PGO优化

GCC局部结构与个体变量及PGO优化
EN

Stack Overflow用户
提问于 2021-05-10 19:59:34
回答 1查看 64关注 0票数 2

我们正在尝试重构解释器循环SWI。这是一个巨大的函数,使用GCC的标签地址来引用虚拟机指令。这个函数有许多VM寄存器变量。事实证明,一个人的写作是有区别的

代码语言:javascript
运行
复制
PL_next_solution(qid_t qid)
{ type1 v1;
  type2 v2;
  // 13 in total

  // lots of code
}

代码语言:javascript
运行
复制
PL_next_solution(qid_t qid)
{ struct
  { type1 v1;
    type2 v2;
    // 13 in total
  } registers;

  // lots of code
}

如果没有剖面引导优化,结果程序的性能差异很小(<5%),而PGO优化的性能差异约为20%。这是相当出乎意料的。这一切为什么要发生?gcc是否尊重这个结构的记忆布局,尽管它在这个功能之外并不为人所知?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-11 14:53:18

根据您的进一步测试,性能影响似乎来自于获取一个或多个struct成员的地址并将其传递给编译器看不到的函数。我相信,当您这样做时,编译器必须假设整个结构都有转义。这就抑制了许多可能的优化:这意味着结构必须放置在内存中,而且每个函数调用都必须将成员存储到该内存中并从内存中重新加载。

为了了解原因,请看一个这样的例子:

代码语言:javascript
运行
复制
void foo(int *);
void bar(void);
struct qux { 
    int a,b,c;
    char huge[5000];
};
int fum(void) {
    struct qux s = { 1,2,3 };
    foo(&s.b);
    s.c=4;
    bar();
    return s.a+s.c;
}

试试哥德波特

请注意,即使进行了最大的优化:

  • 未使用的成员huge仍然被分配和初始化。
  • s.c=4涉及到内存存储
  • s.as.c在调用bar()后从内存中重新加载

我认为问题在于,对于所有编译器来说,foo()bar()可能正在执行的函数:

代码语言:javascript
运行
复制
int *global_ptr;
void foo(int *ip) {
    struct qux *qp = (struct qux *)((char *)ip - offsetof(struct qux, b));
    global_ptr = &qp->c;
    printf("%d %d\n", qp->a, qp->huge[1234]);
}

void bar(void) {
    *global_ptr = 37;
}

我相信这样的代码会有很好的定义;foo必须打印出1 0fum必须返回38。当然,程序员需要确保foo只与struct quxb成员的地址一起调用,并且在fum返回后不会再次调用bar(),因为有一个悬空的指针--但是如果他们这样做了,那么代码就会正常工作。

另一方面,如果将fum中的调用更改为foo(NULL),则可以看到一切都消失了:s.huge从未初始化,甚至没有分配,s.b完全消失,s.as.c被优化,return s.a+s.c;不断折叠到return 5;中。在这种情况下,编译器可以确保结构不会“转义”,因此它可以根据自己的意愿进行优化。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67476965

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档