GCC strict aliasing – 嫉妒就是承认自己不如别人

本文深入探讨了GCC编译器的strictaliasing规则,解释了它如何影响代码优化,并通过实例展示了当代码不遵循该规则时可能出现的问题。文章还介绍了如何通过编译选项-fno-strict-aliasing来禁用这一规则,以及这可能带来的性能影响。

GCC strict aliasing – 嫉妒就是承认自己不如别人

事情是这样的。我们对tair(淘宝的分布式Key/Value系统)动了一次大手术,更换了网络框架,经过长时间的测试/调试,终于完全通过了回归测试。但要打包发布的时候,却发现服务器可以正常启动,但却完全无法接受请求。调试无果,对比打包前后程序的差异,仅在于是否使用-O2选项对程序进行编译优化。
无头苍蝇一样,Google搜索“gcc optimization problems”,找到StackOverflow上面的这个帖子,“抱着试试看的心态”,在编译选项中加入-fno-strict-aliasing,bingo!
-fno-strict-aliasing这个选项是做什么的?aliasing又是什么?C和C++的标准给出了说明:

Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to 
objects of different types will never refer to the same memory location (i.e. alias eachother.)

即是说,在strict aliasing规则下,C/C++编译器认为,“不同类型”的指针(准确说是lvalue)一定不会引用同一个内存区域(即aliasing)。在这个规则的前提下,编译器就可以进行相应的优化。看下面这个函数:

1
2
3
4
5
6
7
8
9
10
int n;
int foo(int *ptr) {
  n=1;
  *ptr=3;
  return n;
}
int main() {
  fprintf(stdout, "%d\n", foo(&n));
  return 0;
}

编译并运行:

1
2
3
4
$ cc main.c && ./a.out
3
$ cc main.c -O2 && ./a.out
3

一切正常,不是吗?但如果把函数foo的参数类型改作double*,运行结果“可能”会是:

1
2
3
4
5
6
$ cc main.c && ./a.out
3
$ cc main.c -O2 && ./a.out
1
$ cc main.c -O2 -fno-strict-aliasing && ./a.out
3

在加-O2选项的情况下程序编译该程序,输出竟然是1,难道*ptr=3没有被执行吗?不是的,*ptr=3确实是执行了的,全局变量n在函数返回时也确实已经是3了(你可以在fprintf之后打印出n值做验证),但是foo函数中的语句return n却被优化成了return 1。为什么呢?因为后者比前者稍了一次内存访问。编译器为什么做这样的优化,为什么在ptr为int*时不做此优化?
这就涉及到strict aliasing的具体规则了。首先定义一下alias:两个不同的变量引用了同一个对象(内存区域),那么就称这两个变量互为alias。下面是C99中可以互为alias的所有情况,除此之外的其他情况下,如果编译时指定-fstrict-aliasing(-O2及以上优化时自动指定),那么就执行strict aliasing:

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
  • a character type.

大致是这样的:两个类型兼容的变量可以互为alias,即使使用了signed/unsigned和const/volatile等修饰符;一个类型可以与另一个包含与该类型兼容的成员的struct/union类型的变量互为alias;char类型可以与任意类型互为alias。C++中可以互为alias的还可以是父类与子类。

可以使用-fno-strict-aliasing来取消strict aliasing规则,同时也就放弃了这个规则带来的优化空间,放弃了一定的性能提升。如果你也遇到了文章开头我遇到的问题,而且担心-fno-strict-aliasing的性能损失,那就只能找出违反规则的代码,调整该代码,或者仅仅取消该代码的strict aliasing。

基本就是这样了,最后总结一下。GCC的aliasing与优化紧密相关,在指定-O2及以上优化级别时自动打开-fstrict-aliasing,执行strict aliasing规则以优化编译程序。如果你的程序不遵守该规则(比如上面foo函数中出现double*ptr和n同时应用n的情况),就极有可能受到惩罚。GCC中与strict aliasing相关的选项除了-fstrict-aliasing/-fno-strict-aliasing,还有warning选项-Wstrict-aliasing=n,这个选项在你违反stict aliasing时给出警告,其中n为检查的力度,一般设为2。

最后,如果想深入了解strict aliasing,可以参考这篇Understanding Strict Aliasing。另外,GCC的官方文档中有和优化选项相关的描述,其中也提到了strict aliasing。

posted on 2012-11-05 10:46  lexus 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2012/11/05/2754743.html

`imgui-master` 和 `imgui-docking` 是 [Dear ImGui](https://github.com/ocornut/imgui) 的不同分支,它们的主要区别在于功能支持和开发状态。以下是对这两个分支的详细解释: --- ### 1. `imgui-master` 这是 ImGui 的官方主分支,托管在 [GitHub](https://github.com/ocornut/imgui) 上。它代表了官方稳定版本的代码库,通常用于生产环境和正式项目。 #### 特点: - **稳定性高**:官方主分支经过测试,适合生产环境使用。 - **功能完整**:包括基本的 UI 控件(按钮、滑块、输入框等)、主题支持、绘图功能等。 - **更新频率适中**:官方更新较为保守,注重稳定性和兼容性。 #### 适用场景: - 项目需要稳定性和兼容性。 - 不需要高级功能(如分屏、拖拽窗口等)。 - 你希望使用官方支持的版本。 --- ### 2. `imgui-docking` `imgui-docking` 是由社区开发的一个分支,主要目标是为 ImGui 添加 **分屏(docking)** 和 **多窗口管理** 功能。这个分支最初由 Omar Cornut(ImGui 的作者)维护,但后来被社区接手并持续开发。 #### 特点: - **支持分屏和拖拽窗口**:这是 `imgui-docking` 最显著的功能。它允许用户将窗口拖动到屏幕的不同区域,并支持窗口的停靠(docking)。 - **更丰富的 UI 布局**:适合需要复杂 UI 布局的项目,例如编辑器、IDE、调试工具等。 - **更新频率高**:社区活跃,功能更新较快,但可能不如官方主分支稳定。 #### 适用场景: - 需要分屏或拖拽窗口功能。 - 开发复杂的 UI 工具(如游戏编辑器、调试器等)。 - 愿意接受一定的不稳定性以换取新功能。 --- ### 如何选择? - 如果你需要一个 **稳定且功能齐全的 UI 库**,并且不需要分屏或拖拽窗口功能,建议使用 `imgui-master`。 - 如果你需要 **高级的 UI 布局功能**(如分屏、拖拽窗口),并且愿意接受一定的不稳定性,建议使用 `imgui-docking`。 --- ### 示例代码 以下是一个简单的 ImGui 示例代码,展示了如何创建一个窗口并添加一些控件。这个代码可以在 `imgui-master` 和 `imgui-docking` 中运行。 ```cpp #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include <GLFW/glfw3.h> int main() { // 初始化 GLFW if (!glfwInit()) { return -1; } // 创建窗口 GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Example", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSwapInterval(1); // 启用垂直同步 // 初始化 ImGui IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); // 初始化平台/渲染器后端 ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 130"); bool show_demo_window = true; bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // 主循环 while (!glfwWindowShouldClose(window)) { glfwPollEvents(); // 开始 ImGuiImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); // 显示 ImGui 的示例窗口 if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); // 创建一个自定义窗口 { static float f = 0.0f; static int counter = 0; ImGui::Begin("Hello, world!"); // 创建一个窗口 ImGui::Text("This is a simple ImGui window."); // 显示文本 ImGui::SliderFloat("Float", &f, 0.0f, 1.0f); // 显示滑块 if (ImGui::Button("Button")) // 显示按钮 counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); ImGui::Checkbox("Demo Window", &show_demo_window); // 显示复选框 ImGui::Checkbox("Another Window", &show_another_window); ImGui::ColorEdit3("Clear Color", (float*)&clear_color); // 显示颜色编辑器 ImGui::End(); } // 如果需要显示另一个窗口 if (show_another_window) { ImGui::Begin("Another Window", &show_another_window); ImGui::Text("This is another window."); ImGui::End(); } // 渲染 ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } // 清理 ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); return 0; } ``` --- ### 代码解释 - **初始化部分**:初始化 GLFW 和 ImGui,设置窗口和上下文。 - **主循环**:在每一帧中,处理输入事件,生成 ImGui 窗口,并渲染。 - **ImGui 窗口**:创建了两个窗口,一个用于显示简单的控件(如按钮、滑块),另一个用于显示额外的内容。 - **渲染部分**:使用 OpenGL 渲染 ImGui 的 UI。 --- ### 相关问题 1. 如何在 ImGui 中实现自定义主题? 2. 如何在 ImGui 中加载和显示图片? 3. 如何将 ImGui 集成到 OpenGL 或 Vulkan 项目中? 4. `imgui-docking` 中的分屏功能是如何实现的?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值