函数的返回值通常存储在堆栈或寄存器中.但对于大型结构,它必须在堆栈上.在这个代码的真实编译器中必须进行多少复制?还是优化了?
例如:
struct Data {
unsigned values[256];
};
Data createData()
{
Data data;
// initialize data values...
return data;
}
Run Code Online (Sandbox Code Playgroud)
(假设函数无法内联..)
c compiler-theory abi calling-convention compiler-optimization
我尝试使用-optimise版本2.7.7的scalac .那时我从来没有得到任何性能改进,但编译花费的时间更长.
Scala 2.9.0中的情况更好吗?国旗目前涵盖哪些优化措施?
compiler-construction performance scala compiler-optimization
当我使用Clang(-O3)或MSVC(/O2)编译并运行此代码时...
#include <stdio.h>
#include <time.h>
static int const N = 0x8000;
int main()
{
clock_t const start = clock();
for (int i = 0; i < N; ++i)
{
int a[N]; // Never used outside of this block, but not optimized away
for (int j = 0; j < N; ++j)
{
++a[j]; // This is undefined behavior (due to possible
// signed integer overflow), but Clang doesn't see it
}
}
clock_t const finish = …Run Code Online (Sandbox Code Playgroud) 编译的一个趋势是使用类型化的中间语言.Haskell的ghc与它的core中间语言,系统F-ω变体,是本体系结构[1]的一个例子.另一个是LLVM,它的核心是一种类型化的中间语言[2].这种方法的好处是可以及早检测构成代码生成器部分的转换中的错误.此外,可以在优化和代码生成期间使用类型信息.
为了提高效率,对类型化的IR进行类型检查,而不是推断它们的类型.为了快速进行类型检查,每个变量和每个活页夹都带有类型以便于类型检查.
但是,编译器管道中的许多转换可能会引入新变量.例如,规范化转换K(.)可能会转换应用程序
M(N)
Run Code Online (Sandbox Code Playgroud)
变成一个表达式
let x = K(M) in
let y = K(N) in x(y)
Run Code Online (Sandbox Code Playgroud)
题.我想知道编译器如何处理给新引入的变量赋予类型的问题.难道他们重新类型检测,在上面的例子中K(M)和K(N)?这不是很费时间吗?它需要通过环境吗?他们是否使用AST节点中的映射来键入信息以避免重新运行类型检查?
S. Marlow,S.Peyton Jones,格拉斯哥Haskell编译器.
compiler-construction ghc intermediate-language compiler-optimization llvm-ir
format_disk如果以下程序从未在代码中调用过程,如何调用它?
#include <cstdio>
static void format_disk()
{
std::puts("formatting hard disk drive!");
}
static void (*foo)() = nullptr;
void never_called()
{
foo = format_disk;
}
int main()
{
foo();
}
Run Code Online (Sandbox Code Playgroud)
这与编译器不同.通过优化启用Clang进行编译,该函数never_called在运行时执行.
$ clang++ -std=c++17 -O3 a.cpp && ./a.out
formatting hard disk drive!
Run Code Online (Sandbox Code Playgroud)
但是,使用GCC进行编译时,此代码只会崩溃:
$ g++ -std=c++17 -O3 a.cpp && ./a.out
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
编译器版本:
$ clang --version
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ gcc --version
gcc (GCC) 7.2.1 20171128
Copyright (C) …Run Code Online (Sandbox Code Playgroud) 关于效率,有谁知道编译器是否足够聪明,无法1, 3, 5在以下代码中创建不包含循环的每次迭代的数组?
var foo = new List<int> { 1, 2, 3, 4, 5 };
foo.RemoveAll(i => new[] { 1, 3, 5 }.Contains(i));
Run Code Online (Sandbox Code Playgroud)
我更喜欢它是为了提高可读性,但不是出于性能考虑。
如果 C 程序有未定义的行为,任何事情都可能发生。因此编译器可能会假设任何给定的程序不包含 UB。因此,假设我们的程序包含以下内容:
\nx += 5;\n/* Do something else without x in the meantime. */ \nx += 7;\nRun Code Online (Sandbox Code Playgroud)\n当然,这可以优化为
\n/* Do something without x. */\nx += 12;\nRun Code Online (Sandbox Code Playgroud)\n或类似的其他方式。
\n如果 x 具有类型,unsigned int则上述程序中不可能出现 UB。另一方面,如果 x 有类型signed int,则有可能溢出,从而产生 UB。由于编译器可能会假设我们的程序不包含UB,因此我们可以进行与上面相同的优化。事实上,在这种情况下,编译器甚至可以假设x - 12 <= MAX_INT.
然而,这似乎与 Jens Gustedt 著名的“Modern C”(第 42 页)相矛盾:
\n\n\n但这样的优化也可以被禁止,因为编译器无法证明某个操作不会强制程序终止。在我们的示例中,很大程度上取决于 x 的类型。如果 x 的当前值可能接近类型的上限,则看似无辜的操作 x += 7 可能会产生溢出。此类溢出根据类型的不同而有不同的处理方式。正如我们所看到的,无符号类型的溢出不是问题,并且压缩运算的结果将始终与两个单独的结果一致。对于其他类型,例如有符号整数类型(signed)和浮点类型(double),溢出可能会引发异常并终止程序。在这种情况下,无法执行优化。
\n
(强调我的)。如果编译器可以(并且确实)假设我们的程序没有 UB,为什么不能执行此优化?
\n …c optimization integer-overflow compiler-optimization undefined-behavior
我有一个C++项目,它使用CMake作为它的构建系统.我想要以下行为:
如果调用cmake cmake ..,CMAKE_CXX_FLAGS则为-O3 -Wall -Wextra
如果调用cmake cmake .. -DCMAKE_BUILD_TYPE=Debug,CMAKE_CXX_FLAGS则为-g -Wall -Wextra
我尝试了以下内容
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
Run Code Online (Sandbox Code Playgroud)
但这有一个很大的问题.首先,如果使用第二次调用,则将两者-O3和-g标志传递给编译器.此外,如果我使用第二次调用和之后的第一次调用,虽然没有明确排序,但CMAKE_BUILD_TYPE仍然保留Debug- 所以我得到一个Debug构建,尽管我想要一个优化的构建.
为什么?我该怎么做才能获得理想的行为?
我注意到我的电脑上有一个奇怪的东西。*手写的可分性测试明显比%算子快。考虑最小的例子:
* AMD 锐龙 Threadripper 2990WX,GCC 9.2.0
static int divisible_ui_p(unsigned int m, unsigned int a)
{
if (m <= a) {
if (m == a) {
return 1;
}
return 0;
}
m += a;
m >>= __builtin_ctz(m);
return divisible_ui_p(m, a);
}
Run Code Online (Sandbox Code Playgroud)
该示例受奇数a和 的限制m > 0。然而,它可以很容易地推广到所有a和m。代码只是将除法转换为一系列加法。
现在考虑使用以下命令编译的测试程序-std=c99 -march=native -O3:
for (unsigned int a = 1; a < 100000; a += 2) {
for (unsigned int m …Run Code Online (Sandbox Code Playgroud) 我发现手动计算%运算符__int128比内置的编译器运算符要快得多。我将向您展示如何计算模 9,但该方法可用于计算模任何其他数字。
首先,考虑内置编译器操作符:
uint64_t mod9_v1(unsigned __int128 n)
{
return n % 9;
}
Run Code Online (Sandbox Code Playgroud)
现在考虑我的手动实现:
uint64_t mod9_v2(unsigned __int128 n)
{
uint64_t r = 0;
r += (uint32_t)(n);
r += (uint32_t)(n >> 32) * (uint64_t)4;
r += (uint32_t)(n >> 64) * (uint64_t)7;
r += (uint32_t)(n >> 96);
return r % 9;
}
Run Code Online (Sandbox Code Playgroud)
测量超过 100,000,000 个随机数给出以下结果:
mod9_v1 | 3.986052 secs
mod9_v2 | 1.814339 secs
Run Code Online (Sandbox Code Playgroud)
GCC 9.3.0 with-march=native -O3用于 AMD Ryzen Threadripper 2990WX。
这是godbolt的链接。
我想问一下它在你这边的行为是否相同?(在向 GCC Bugzilla …