首页 / 知识
为什么C / C ++程序经常在调试模式下关闭优化?
2023-04-15 17:52:00

Why does a C/C++ program often have optimization turned off in debug mode?
在大多数C或C ++环境中,存在"调试"模式和"发行"模式编译。
如果不进行任何优化,则代码流是线性的。如果您位于第5行并且是单步执行,那么您将跳至第6行。启用优化后,您可以进行指令重新排序,循环展开以及各种优化。
在此示例中,如果没有优化,则可以单步执行代码并按第1、2、3、2、3、2、4行 启用优化后,您可能会获得如下所示的执行路径:2、3、3、4甚至只是4! (该功能根本不执行任何操作...) 最重要的是,启用优化功能的调试代码可能会很痛苦!特别是如果您具有大型功能。 请注意,启用优化会更改代码!在某些环境(安全关键系统)中,这是不可接受的,要调试的代码必须是出厂的代码。在这种情况下,必须进行调试并进行优化。
尽管优化和未优化的代码应在功能上等效,但在某些情况下,行为将发生变化。
禁用优化后,这很简单,您就知道会发生什么。
在所有这些情况下,行为将截然不同,并且很可能是错误的。 调试和发布之间的另一个关键区别是局部变量的存储方式。从概念上讲,局部变量是在函数堆栈框架中分配存储的。编译器生成的符号文件告诉调试器堆栈框架中变量的偏移量,以便调试器可以将其显示给您。调试器偷看内存位置以执行此操作。 但是,这意味着每次更改局部变量时,为该源代码行生成的代码都必须将值写回到堆栈上的正确位置。由于内存开销,这是非常低效的。 在一个发行版中,编译器可以将局部变量分配给部分函数的寄存器。在某些情况下,它可能根本不为其分配堆栈存储空间(一台计算机拥有的寄存器越多,执行此操作就越容易)。 但是,调试器不知道寄存器如何映射到代码中特定点的局部变量(我不知道任何包含此信息的符号格式),因此它无法像它那样准确地向您显示不知道去哪里找它。 另一个优化是函数内联。在优化的版本中,编译器可能会在每次使用foo()的地方将其替换为foo()的实际代码,因为该函数足够小。但是,当您尝试在foo()上设置断点时,调试器希望知道foo()的指令地址,并且对此不再有简单的答案-可能有成千上万的foo()副本。 )代码字节分布在您的程序中。调试版本将确保您可以在某个地方放置断点。 优化代码是一个自动化的过程,可以在保留语义的同时提高代码的运行时性能。此过程可以删除中间结果,这些中间结果对于完成表达式或函数求值是不必要的,但是在调试时可能会引起您的兴趣。类似地,优化可以改变外观上的控制流,从而事情发生的顺序可能与源代码中出现的顺序略有不同。这样做是为了跳过不必要或多余的计算。代码的这种重新编排可能会使源代码行号和目标代码地址之间的映射混乱,从而使调试器难以在编写代码时遵循控制流。 在未优化模式下进行调试使您可以在编写过程中看到自己编写的所有内容,而无需优化程序删除或重新排列内容。 对程序正常运行感到满意后,就可以打开优化以提高性能。即使这些天优化器是值得信赖的,但构建一个高质量的测试套件以确保您的程序在优化和未优化模式下运行相同(从功能的角度,不考虑性能)仍然是一个好主意。 期望将调试版本调试!如果每行非空,无注释的源代码都匹配某些机器代码指令,则设置断点,单步执行变量,堆栈跟踪以及在调试器中进行的其他所有操作(IDE或其他方式)都是有意义的。 大多数优化都与机器代码的顺序混淆。循环展开就是一个很好的例子。常见的子表达式可以取消循环。启用优化(即使是最简单的级别),您可能正在尝试在机器代码级别不存在的行上设置断点。有时您无法监视局部变量,因为它被保存在CPU寄存器中,甚至可能已经过优化而已! 优化的另一个问题是内联函数,从某种意义上说,您总是会单步执行它们。
使用GCC,同时启用调试和优化功能,如果您不知道要期待什么,您会认为代码行为不正常,并多次重复执行同一条语句-这是我的几个同事发生的。 但是,在虚拟机(如Java)托管的语言中,优化和调试可以共存-即使在调试过程中,仍将JIT编译为本机代码,并且仅将调试方法的代码透明地转换为未优化的版本。 我想强调的是,除非使用的优化器有错误,或者代码本身有错误并且依赖于部分未定义的语义,否则优化不应更改代码的行为。后者在多线程编程中或在使用内联汇编时更为常见。
至少在Linux上(没有理由Windows应该有所不同),调试信息打包在二进制文件的单独部分中,并且在正常执行期间不会加载。可以将它们拆分为其他文件以用于调试。 如果您是在指令级别而不是源代码级别进行调试,那么这很麻烦,因为您可以更轻松地将未优化的指令映射回源代码。同样,编译器有时在优化器中也有漏洞。 在Microsoft的Windows部门中,所有发行二进制文件都带有调试符号和完整的优化功能。这些符号存储在单独的PDB文件中,不会影响代码的性能。它们不随产品一起提供,但是大多数都可以从Microsoft Symbol Server获得。 |
最新内容
相关内容
linux命令行模式登录?
linux命令行模式登录?,系统,密码,信息,状态,情况,终端,环境,管理,电脑,位置,linux,按了ctrl+alt+F1,进入了类似纯命令行的界面,让输入login信linux退出命令模式?
linux退出命令模式?,状态,档案,命令,环境,密码,终端,文件,模式,编辑,端口,linux退出vi编辑命令先按ESC进入Command模式,然后输入“:wq”,回车就可linux命令模式加行?
linux命令模式加行?,工作,地址,系统,命令,工具,正规,信息,时间,第一,模式,Linux下vim编辑器命令大全杀死进程killvi命令vi/vim是linux最常用的linux命令行模式使用?
linux命令行模式使用?,工作,地址,目录,信息,名称,系统,管理,命令,文件,标准,linux常用命令1、linux系统常用操作命令如下:ls:全拼list,功能是列出linux中编译命令是?
linux中编译命令是?,系统,代码,工具,基础,名字,命令,编译器,文件,终端,源程序,要怎么在linux系统中编译并运行c程序1、在Linux下面,如果要编译linux源码编译命令?
linux源码编译命令?,代码,设备,环境,工作,官网,最新,下来,电脑,软件,项目,如何编译armlinux的go?第一次编译时用的是root用户(第二次用一般用户linux终端编译命令?
linux终端编译命令?,代码,工具,环境,系统,终端,文件,源程序,编译器,语言,指令,linux终端下如何进行C语言编译在Linux下面,如果要编译一个C语言linux切换成命令模式?
linux切换成命令模式?,密码,系统,工具,模式,命令,首页,状态,图形界面,终端,界面,Linux切换命令模式与GUI模式命令行→图形 startx 注:图形界面linux命令行模式清页?
linux命令行模式清页?,工作,系统,命令,信息,地址,目录,内容,文件,操作,功能,linux常用命令linux系统常用操作命令如下:ls:全拼list,功能是列出目linux进入命令行模式?
linux进入命令行模式?,系统,地址,情况,工作,命令,终端,首页,信息,目录,界面,linux怎么进入字符命令界面??1、打开linux系统,在linux的桌面的空linux命令行编译文件?
linux命令行编译文件?,代码,系统,项目,工具,网上,手机,电脑,地址,官网,文件,Linux下,用什么命令编译m文件为可执行文件所以使用gcc test -o telinux系统编译命令?
linux系统编译命令?,系统,代码,百度,暂停,电脑,工具,命令,终端,内核,程序,Linux能直接编译C语言吗?1、在Linux下面,如果要编译一个C语言源程序,