问题标签 [compiler-optimization]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c# - c#:这段代码会得到优化吗?
我正在查看第三方外包公司提供给我们的一些代码,并遇到了这个小宝石:
我的问题是编译器会完全优化这个吗?当我在 Reflector 中查看已编译的程序集时,它会显示:
i 的声明已移至方法的顶部,并且 Exception 类型的附加声明也在方法的顶部。
所以,由于这段代码并没有真正做任何事情,我想知道编译器是否足够聪明,可以看到这段代码什么都不做并且可以优化它。
c++ - C++:如何知道基类 SubObject 的大小?
.
在这里,我讨论了空基优化,MSalters发表了这个有趣的评论:
任何类都不能有 sizeof(Class)==0,无论是否为空。但是我们专门讨论的是空基类子对象的大小。 它不需要自己的 vtable,也不需要 vtable 指针。假设一个 vtable 指针在偏移量 0 处的通用布局;这将导致零大小的基类子对象与派生类共享其 vtable 指针。没问题:无论如何,它们应该是相同的,这几乎就是虚函数的重点。
我的具体问题是:当我们使用 Empty Class 作为基类时,编译器可能会优化,也可能不会。我们如何确定它实际上做了什么?
一般来说,我们如何知道基类子对象的大小?无论我们是否将其用作基础,基础子对象的大小是否相同?编译器是否仅使用空基类进行优化?
.
c++ - C++:编译器以多少种方式优化我们的代码?
.
我想知道编译器可以/确实优化我们用 C++ 编写的代码的所有可能方式(或至少流行的方式)?我也想知道优化是如何完成的(在每种情况下)!
到目前为止,我知道两个优化,即。空基优化 (EBO) 和返回值优化 (RVO)。还有什么?我听说过“const”优化,“unused variable”优化。这些是什么?
.
c++ - 本地实现的接口引用
请考虑以下代码:
在 GCC 4.4.1(使用-O2
)下,对的调用B::go()
被内联(即,没有发生虚拟调度)。这意味着编译器承认a_ref
确实指向了一个B
类型变量。B
引用可用于指向 a ,C
但编译器足够聪明,可以预见到情况并非如此,因此它完全优化了函数调用,内联函数。
伟大的!这是一个令人难以置信的优化。
但是,那么,为什么 GCC 在以下情况下不做同样的事情呢?
有任何想法吗?其他编译器呢?这种优化常见吗?(我对这种编译器洞察力很陌生,所以我很好奇)
如果第二种情况有效,我可以创建一些非常棒的模板,例如:
在许多情况下,这些模板可用于避免虚拟调度:
实施它是否可行?(不要继续说这是一个微优化,因为它很可能是一个巨大的优化..)
- 编辑
我只是注意到问题与static_ptr<>
我暴露的问题无关。指针类型被保留,但它仍然没有内联。我猜 GCC 并没有深入到找出 static_ptr_container<>::value 不是引用也不是指针的程度。对于那个很抱歉。但这个问题仍然没有答案。
- 编辑
我已经制定了一个static_ptr<>
真正有效的版本。我也稍微改了一下名字:
唯一的弱点是用户必须访问ptr->value
才能获取实际对象。重载operator ->()
在 GCC 中不起作用。任何返回对实际对象的引用的方法(如果它是内联的)都会破坏优化。太遗憾了..
c++ - 进程被杀死时如何获取 .gcda 文件?
我有一个带有-fprofile-arcs
和的二进制构建-ftest-coverage
。该二进制文件由进程监视器运行,该进程监视器将该进程作为子进程生成。然后,当我希望进程退出时,我必须通过进程监视器。它向SIGKILL
进程发送一个。我发现.gcda
在这种情况下不会生成文件。我能做些什么?
编辑:实际上进程监视器首先尝试使进程退出。但是,ProcessMonitor 库(在每个进程中使用)调用_exit
而不是exit
在用户发出命令停止进程时调用。这是一切麻烦的根源。
c++ - 为什么我们使用 volatile 关键字?
可能重复:
为什么 volatile 存在?
我从未使用过它,但我想知道为什么人们使用它?它究竟是做什么的?我搜索了论坛,我发现它只有 C# 或 Java 主题。
algorithm - 内联算法
有谁知道任何讨论内联算法的论文?和密切相关的是,父子图与调用图的关系。
背景:我编写了一个编译器,Ocaml
其中积极地内联函数,主要是由于这个和其他一些优化,它为我的编程语言在许多情况下(包括甚至C
)生成比大多数其他语言更快的代码。
问题 #1:该算法在递归方面存在问题。为此,我的规则只是将孩子内联到父母中,以防止无限递归,但这排除了兄弟函数相互内联一次。
问题 #2:我不知道优化内联操作的简单方法。我的算法对于函数体的可变表示是必不可少的,因为似乎根本不可能做出有效的函数内联算法。如果调用图是一棵树,很明显自下而上的内联是最佳的。
技术信息:内联由许多内联步骤组成。问题是步骤的顺序。
每个步骤的工作原理如下:
- 我们通过用实参替换类型参数和值参数来制作要内联和 beta-reduce 的函数的副本。
- 然后,我们将 return 语句替换为对新变量的赋值,然后跳转到函数体的末尾。
- 然后,对该函数的原始调用将被此主体替换。
- 然而我们还没有完成。我们还必须克隆该函数的所有子函数,同时对它们进行 beta 缩减,并将克隆重新作为调用函数的父级。
克隆操作使得内联递归函数变得极其困难。保留已在进行中的内容的列表并仅检查我们是否已经在处理此调用的常用技巧在幼稚的形式中不起作用,因为递归调用现在被移动到被填充到调用中的 beta-reduced 代码函数,并且递归目标可能已更改为克隆的孩子。然而,在调用父级时,子级仍在调用调用其子级的原始父级,现在递归的展开不会停止。如前所述,我打破了这种回归,只允许内联对孩子的递归调用,防止兄弟递归被内联。
由于需要garbage collect
未使用的函数,内联的成本变得更加复杂。由于内联可能是指数级的,因此这是必不可少的。如果对一个函数的所有调用都是内联的,如果该函数还没有被内联,我们应该删除它,否则我们将浪费时间内联到一个不再使用的函数。实际上跟踪谁调用了什么是极其困难的,因为当内联时,我们不是在使用实际的函数表示,而是一个“未分解”的表示:例如,指令列表正在按顺序处理并建立一个新列表,并且在任何一个时间点都可能没有一个连贯的指令列表。
在他的 ML 编译器中,Steven Weeks 选择使用一些重复应用的小优化,因为这使得优化易于编写和控制,但不幸的是,与递归算法相比,这错过了很多优化机会。
问题 #3:什么时候内联函数调用是安全的?
笼统地解释这个问题:在惰性函数式语言中,参数被包装在闭包中,然后我们可以内联应用程序;这是 Haskell 的标准模型。然而,它也解释了为什么Haskell
这么慢。如果参数已知,则不需要闭包,则可以直接将参数替换为其参数 where is 出现(这是正常顺序beta-reduction
)。
但是,如果已知参数评估不是非终止的,则可以使用急切评估:为参数分配一次表达式的值,然后重用。这两种技术的混合是使用闭包,但将结果缓存在闭包对象中。尽管如此,GHC 还没有成功地生成非常高效的代码:这显然非常困难,尤其是在您进行单独编译的情况下。
在菲利克斯,我采取了相反的方法。我不是通过证明优化保留语义来要求正确性并逐渐提高效率,而是要求优化定义语义。这保证了优化器的正确操作,但代价是某些代码将如何表现的不确定性。这个想法是为程序员提供方法来强制优化器在默认优化策略过于激进的情况下符合预期的语义。
例如,默认的参数传递模式允许编译器选择是否将参数包装在闭包中,用参数替换参数,或者将参数分配给参数。如果程序员想要强制闭包,他们可以传入一个闭包。如果程序员想要强制进行急切评估,他们会标记参数var
。
这里的复杂性远大于函数式编程语言:Felix 是一种带有变量和指针的过程语言。它也有 Haskell 风格的类型类。这使得内联例程极其复杂,例如,类型类实例尽可能替换抽象函数(由于调用多态函数时的类型特化,可能在内联时找到实例,所以现在我们有了一个新函数可以内联)。
为了清楚起见,我必须添加更多注释。
内联和其他一些优化,例如用户定义的术语减少、类型类实例化、用于变量消除的线性数据流检查、尾部记录优化,都是在给定函数上一次性完成的。
排序问题不是应用不同优化的顺序,问题是对函数进行排序。
我使用一种脑死亡算法来检测递归:我建立一个每个函数直接使用的所有内容的列表,找到闭包,然后检查函数是否在结果中。请注意,在优化过程中会多次建立使用集,这是一个严重的瓶颈。
不幸的是,函数是否递归可能会发生变化。递归函数可能在尾部记录优化后变为非递归函数。但是有一个更难的情况:实例化一个类型类的“虚拟”函数可以使看起来是非递归的递归。
至于兄弟调用,问题是给定 f 和 g 其中 f 调用 g 和 g 调用 f 我实际上想将 f 内联到 g 中,并将 g 内联到 f .. 一次。我的无限回归停止规则是,如果 f 是 g 的孩子,则只允许将 f 内联到 g 中,这不包括内联兄弟。
基本上我想“尽可能地”“扁平化”所有代码。
c++ - STL 算法中跨函数边界的循环重新排序
为简单起见,假设我N
每M
行都有一个矩阵向量。我正在使用 STLstd::accumulate
来计算所有矩阵的总和。我传递了一个二进制函子,它接受两个矩阵(通过引用)并返回它们的总和(通过引用)。全面披露:我正在使用 libstdc++ 并行模式。在仿函数内部,我分别遍历行以计算总和。
尽管每个矩阵都太大而无法放入缓存中,但一行非常适合。因此,重新排序循环将是有利的,以便外部循环索引M
行,而内部循环索引N
矩阵。除了定义内联函子之外,我还能做些什么来鼓励这种跨功能边界循环重新排序。我当然可以重构代码,但理想情况下我希望保持使用 STL 算法提供的简单结构。如果有特定于 gcc 的东西,我也不介意。
我实际上并不是在处理矩阵,这只是一个例子,但同样的问题结构也适用。主要问题是性能问题。解释实际场景太麻烦了,但核心问题是:STL 的累加需要在嵌套循环之间进行排序,这对缓存不是很友好,因为它会在移动到下一个对象之前尝试完成两个对象的相加。单个对象太大而无法保存在缓存中,但它的一部分可以。因此,如果一次计算“加法”一个“部分”(在所有对象上),则可以加快执行速度。手动重新排序循环可以显着提高 FLOPS。但理想情况下,我希望编译器进行重新排序,以便我可以在 STL 级别(尽可能)进行编码。所以我正在寻找技巧来做到这一点。
c++ - 如何避免线程 + 优化器 == 无限循环?
在今天的代码审查中,我偶然发现了以下代码(为发布而稍作修改):
这是在新线程中运行的前几行代码。在另一个线程中,一旦初始化完成,它就会设置initialized
为true
.
我知道优化器可以把它变成一个无限循环,但是避免这种情况的最好方法是什么?
volatile
-被认为是有害的- 调用
isInitialized()
函数而不是直接使用变量 - 这会保证内存屏障吗?如果函数被声明了inline
怎么办?
还有其他选择吗?
编辑:
应该早点提到这一点,但这是需要在 Windows、Linux、Solaris 等上运行的可移植代码。我们主要使用Boost.Thread作为我们的可移植线程库。
compiler-construction - 定位独立代码和共享对象
任何人都请解释位置独立代码和共享对象的实际含义。编译后创建的目标代码是否与位置无关。
如果有人解释如何在与位置无关的代码中处理对本地/外部变量的访问,那将会很有帮助。
还寻找一些现实生活中的动态链接/加载示例/情况。