【问题标题】:GCC local struct vs individual variables and PGO optimziationGCC 局部结构与单个变量和 PGO 优化
【发布时间】:2021-08-01 05:48:49
【问题描述】:

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

PL_next_solution(qid_t qid)
{ type1 v1;
  type2 v2;
  // 13 in total

  // lots of code
}

PL_next_solution(qid_t qid)
{ struct
  { type1 v1;
    type2 v2;
    // 13 in total
  } registers;

  // lots of code
}

如果没有配置文件引导优化,最终程序的性能差异很小 (

【问题讨论】:

  • 有什么区别?
  • 对不起,结果程序的性能
  • 运行时的性能?还是内存消耗?
  • registers变量的地址被占用了吗?并通过某个地方? the difference is about 20%erm,哪个更快?
  • 我认为需要更多代码

标签: c gcc struct


【解决方案1】:

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

要了解原因,请看如下示例:

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;
}

Try on godbolt

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

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

我认为关键在于,就编译器所知,foo()bar() 函数可能正在执行:

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。当然,程序员需要确保只使用 struct quxb 成员的地址调用 foo,并且bar()fum 返回后不会再次调用,因为悬空指针 - 但如果他们这样做,那么代码应该可以工作。

另一方面,如果您将fum 中的调用更改为foo(NULL),您会看到一切都消失了:s.huge 从未初始化甚至分配,s.b 完全消失,s.a 和@ 987654345@ 被优化掉,return s.a+s.c; 被常量折叠成return 5;。在这种情况下,编译器可以确保结构永远不会“转义”,因此可以随意优化。

【讨论】:

    猜你喜欢
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 2011-07-12
    相关资源
    最近更新 更多