标签: compiler-optimization

技术原因是C#没有发出"尾巴".CIL指令?

可能重复:
为什么.net/C#不会消除尾递归?

请使用以下C#代码:

using System;

namespace TailTest
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            Counter(0);
        }

        static void Counter(int i)
        {
            Console.WriteLine(i);
            if (i < int.MaxValue) Counter(++i);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

C#编译器(无论如何)我会将Counter方法编译成以下CIL:

.method private static hidebysig default void Counter (int32 i) cil managed 
{
.maxstack 8
IL_0000:  ldarg.0 
IL_0001:  call void class [mscorlib]System.Console::WriteLine(int32)
IL_0006:  ldarg.0 
IL_0007:  ldc.i4 2147483647
IL_000c:  bge IL_0019
IL_0011:  ldarg.0 
IL_0012:  ldc.i4.1 
IL_0013:  add 
IL_0014:  call void class TailTest.MainClass::Counter(int32)
IL_0019:  ret 
}
Run Code Online (Sandbox Code Playgroud)

上面代码的问题是它会导致堆栈溢出(在我的硬件上约为i = …

.net c# mono compiler-optimization tail-call-optimization

26
推荐指数
2
解决办法
1305
查看次数

返回值优化和副作用

返回值优化(RVO)是一种涉及复制省略的优化技术,它消除了在某些情况下为保存函数返回值而创建的临时对象.我一般都了解RVO的好处,但我有几个问题.

该标准在本工作草案的第12段第32段(强调我的)中说明了以下内容.

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用.在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间.没有优化就被破坏了.

然后,当实现可以执行此优化时,它会列出许多条件.


关于这种潜在的优化,我有几个问题:

  1. 我习惯于约束优化,以至于它们无法改变可观察的行为.此限制似乎不适用于RVO.我是否需要担心标准中提到的副作用?是否存在可能导致问题的角落案例?

  2. 作为程序员,我需要做什么(或不做)才能执行此优化?例如,以下是否禁止使用复制省略(由于move):

std::vector<double> foo(int bar){
    std::vector<double> quux(bar,0);
    return std::move(quux);
}
Run Code Online (Sandbox Code Playgroud)

编辑

我将此作为一个新问题发布,因为我提到的具体问题在其他相关问题中没有直接回答.

c++ compiler-optimization c++11

26
推荐指数
2
解决办法
1738
查看次数

在代码优化期间,C++ 11编译器能否将局部变量转换为右值?

有时将复杂或长表达式分成多个步骤是明智的(例如,第二个版本不是更清楚,但它只是一个例子):

return object1(object2(object3(x)));
Run Code Online (Sandbox Code Playgroud)

可以写成:

object3 a(x);
object2 b(a);
object1 c(b);
return c;
Run Code Online (Sandbox Code Playgroud)

假设所有3个类都实现了以rvalue作为参数的构造函数,第一个版本可能更快,因为临时对象被传递并可以移动.我假设在第二个版本中,局部变量被认为是左值.但是如果以后没有使用变量,那么C++ 11编译器是否会优化代码,因此变量被认为是rvalues,两个版本的工作方式完全相同?我最感兴趣的是Visual Studio 2013的C++编译器,但我也很高兴知道GCC编译器在这个问题上的行为.

谢谢,米哈尔

c++ compiler-optimization rvalue-reference visual-c++ c++11

26
推荐指数
4
解决办法
2092
查看次数

为什么.NET JIT编译器决定不内联或优化掉没有副作用的空静态方法的调用?

我认为我正在观察.NET JIT编译器没有内联或优化对没有副作用的空静态方法的调用,考虑到一些直言不讳的在线资源,这有点令人惊讶.

我的环境是x64,Windows 8.1,.NET Framework 4.5上的Visual Studio 2013.

鉴于这个简单的测试程序(https://ideone.com/2BRCpC)

class Program
{
    static void EmptyBody()
    {
    }

    static void Main()
    {
        EmptyBody();
    }
}
Run Code Online (Sandbox Code Playgroud)

发布版本与上述程序的最优化产生以下MSIL为MainEmptyBody:

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  call       void Program::EmptyBody()
  IL_0005:  ret
} // end of method Program::Main

.method private hidebysig static void  EmptyBody() cil managed
{
  // Code size       1 (0x1)
  .maxstack  8
  IL_0000:  ret
} // …
Run Code Online (Sandbox Code Playgroud)

c# clr compiler-optimization

26
推荐指数
1
解决办法
1610
查看次数

重写为GHC中的实用优化技术:它真的需要吗?

我正在阅读Simon Peyton Jones等人撰写的论文.命名为"遵循规则:重写为GHC中的实用优化技术".在第二部分,即他们写的"基本思想":

考虑熟悉的map函数,它将函数应用于列表的每个元素.写在Haskell中,map看起来像这样:

map f []     = []
map f (x:xs) = f x : map f xs
Run Code Online (Sandbox Code Playgroud)

现在假设编译器遇到以下调用map:

map f (map g xs)
Run Code Online (Sandbox Code Playgroud)

我们知道这个表达式相当于

map (f . g) xs
Run Code Online (Sandbox Code Playgroud)

(其中"."是函数组合),我们知道后一个表达式比前者更有效,因为没有中间列表.但是编译器没有这样的知识.

一个可能的反驳是编译器应该更聪明---但程序员总是会知道编译器无法弄清楚的事情.另一个建议是:允许程序员将这些知识直接传递给编译器.这是我们在这里探索的方向.

我的问题是,为什么我们不能让编译器变得更聪明?作者说"但程序员总是会知道编译器无法弄清楚的东西".但是,这不是一个有效的答案,因为编译器确实可以找出map f (map g xs)相当于的map (f . g) xs,这里是如何:

map f (map g xs)
Run Code Online (Sandbox Code Playgroud)
  1. map g xs与...结合map f [] = [].

    因此map g [] = [].

  2. map f (map g …

optimization haskell ghc compiler-optimization rewriting

26
推荐指数
2
解决办法
706
查看次数

为什么C ++编译器可能会复制一个函数退出基本块?

考虑以下代码片段:

int* find_ptr(int* mem, int sz, int val) {
    for (int i = 0; i < sz; i++) {
        if (mem[i] == val) { 
            return &mem[i];
        }
    }
    return nullptr;
}
Run Code Online (Sandbox Code Playgroud)

-O3上的GCC将其编译为:

find_ptr(int*, int, int):
        mov     rax, rdi
        test    esi, esi
        jle     .L4                  # why not .L8?
        lea     ecx, [rsi-1]
        lea     rcx, [rdi+4+rcx*4]
        jmp     .L3
.L9:
        add     rax, 4
        cmp     rax, rcx
        je      .L8
.L3:
        cmp     DWORD PTR [rax], edx
        jne     .L9
        ret
.L8:
        xor     eax, eax
        ret
.L4: …
Run Code Online (Sandbox Code Playgroud)

c++ gcc x86-64 compiler-optimization

26
推荐指数
1
解决办法
500
查看次数

为什么修改另一个变量引用的字段会导致意外行为?

我写的这段代码乍一看很简单。它修改被引用变量引用的变量,然后返回引用的值。重现奇怪行为的简化版本如下所示:

#include <iostream>
using std::cout;

struct A {
    int a;
    int& b;

    A(int x) : a(x), b(a) {}
    A(const A& other) : a(other.a), b(a) {}
    A() : a(0), b(a) {}
};

int foo(A a) {
    a.a *= a.b;
    return a.b;
}


int main() {
    A a(3);

    cout << foo(a) << '\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,当它在启用优化(g++ 7.5)的情况下编译时,它会产生与未优化代码不同的输出(即 9 个没有优化 -正如预期的那样,3 个启用了优化)。

我知道volatile关键字,它可以防止编译器在存在某些副作用(例如异步执行和特定于硬件的东西)的情况下重新排序和其他优化,并且在这种情况下也有帮助。

但是,我不明白为什么在这种特殊情况下需要将引用 b 声明为 volatile ?这段代码的错误来源在哪里?

c++ gcc g++ volatile compiler-optimization

26
推荐指数
1
解决办法
500
查看次数

针对C标准的条件移动优化是什么?

使用条件移动(汇编cmov)来优化?:C中的条件表达式是一种常见的优化.但是,C标准说:

第一个操作数被评估; 在其评估与第二或第三操作数的评估之间存在一个序列点(以评估者为准).仅当第一个操作数不等于0时才评估第二个操作数; 仅当第一个操作数比较等于0时才评估第三个操作数; 结果是第二个或第三个操作数的值(无论哪个被评估),转换为下面描述的类型.110)

例如,以下C代码

#include <stdio.h>

int main() {
    int a, b;
    scanf("%d %d", &a, &b);
    int c= a > b ? a + 1 : 2 + b;
    printf("%d", c);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

将生成优化的相关asm代码,如下所示:

call    __isoc99_scanf
movl    (%rsp), %esi
movl    4(%rsp), %ecx
movl    $1, %edi
leal    2(%rcx), %eax
leal    1(%rsi), %edx
cmpl    %ecx, %esi
movl    $.LC1, %esi
cmovle  %eax, %edx
xorl    %eax, %eax
call    __printf_chk
Run Code Online (Sandbox Code Playgroud)

根据标准,条件表达式将仅评估一个分支.但是这里对两个分支进行了评估,这违反了标准的语义.这是针对C标准的优化吗?或者许多编译器优化是否与语言标准不一致?

c assembly ternary-operator compiler-optimization language-lawyer

25
推荐指数
2
解决办法
2408
查看次数

Java编译器是否优化了不必要的三元运算符?

我一直在审查代码,其中一些编码器一直在使用冗余三元运算符"以提高可读性."例如:

boolean val = (foo == bar && foo1 != bar) ? true : false;
Run Code Online (Sandbox Code Playgroud)

显然,将语句的结果分配给boolean变量会更好,但编译器是否关心?

java code-readability compiler-optimization

25
推荐指数
3
解决办法
1757
查看次数

未在代码中调用的函数在运行时调用

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)

c++ g++ compiler-optimization undefined-behavior clang++

24
推荐指数
1
解决办法
2262
查看次数