有可能有一个优化a = func(a)的编译器吗?

时间:2013-07-03 14:31:25

标签: compiler-optimization

假设我有一个类型A的对象。对于A类型的任何函数考虑这种情况 - > A(即获取类型A的对象并返回另一个类型为A的对象):

foo = func(foo)

此处,最简单的情况是将func(foo)的结果复制到foo。 是否可以对此进行优化:

  • foofunc
  • 中进行了修改

使用的语言没有限制。我想知道的是语言必须具有哪些约束和属性来实现这样的优化。是否存在执行此类优化的现有语言?

示例(伪代码):

type Matrix = List<List<int>>

Matrix rotate90Deg(Matrix x):
   Matrix result(x.columns, x.rows) #Assume it has a constructor which takes as args the num of rows, and num of cols.
   for (int i = 0; i < x.rows; i++):
       for (int j = 0; j < x.columns; j++):
           result[i][j] = x[j][i]
   return result

Matrix a = [[1,2,3],[4,5,6],[7,8,9]]
a = rotate90Deg(a)

在这里,是否可以优化代码,使其不为新矩阵(结果)分配内存,而只是修改传递的原始矩阵。

2 个答案:

答案 0 :(得分:2)

首先,您必须意识到某些操作本身不可能就地计算。矩阵 - 矩阵乘法就是一个例子,rotate90Deg属于这个类别,因为这样的操作实际上是适当乘法矩阵的矩阵乘法。

现在,就您的示例而言,您实际编写了一个矩阵转置函数。由于您要交换数字对,因此Matrix转置可以就地完成,但我怀疑任何编译器都可以自动检测并优化它。实际上,为了获得巨大的性能提升,可以使用许多很多技巧来优化矩阵转置,以便对缓存友好。然而,通过一个天真的实现,你几乎肯定会得到与Aditya Kumar在他的回答中描述的非常相似的东西。


正如我之前使用“天真”这个词预示的那样,程序员可以通过高级模板和其他元编程技术,哄骗编译器以极其优化的方式内联大量内容。 (至少在C ++中,也许还有其他允许你重载operator =的语言。)对于那些对如何完成以及涉及的内容进行案例研究感兴趣的人,请查看Eigen matrix library,以及它如何处理像u = v + w;这样的简单操作,其中三个变量都是浮点矩阵。以下是关键点的简要概述。

一个天真的实现会重载operator+以返回临时和operator=来临时复制到结果。当然,在C ++ 11中,通过移动构造函数很容易在赋值期间避免最终复制,但是如果你在右侧有多个运算符,那么你仍然会有不必要的临时副本,如{ {1}}因为每个操作符/方法都会返回一个临时的,并且必须循环该临时操作才能处理下一个操作符。

长话短说,Eigen所做的不是在相应的函数调用发生时执行每个操作,调用返回一个模板化的算子,只是描述操作需要发生,并且所有实际工作最终都发生在u = 3.15f * u.transposed() + 5.0f;中,从而使编译器能够发出一个内联循环,仅用于遍历数据一次并真正就地进行操作。

答案 1 :(得分:1)

是的,这是可能的,这个优化至少由C ++ 11(内联)提供。

稍微解释一下优化。

e.g。

foo_t foo;
foo = func(foo); // #1
foo_t func(foo_t foo1) {
   foo_t new_foo;
   // operate on new_foo by using foo1
   return new_foo;
}

正在制作foo_t的三个实例:

  1. foo被复制并作为foo1传递给func
  2. new_foo已创建。
  3. 通过将new_foo的内容复制到foo; ,将
  4. new_foo分配给foo

    如果存在一些不变量,则可以删除所有三个副本。

    1. foo(传递给函数的参数以后永远不会使用相同的原始值。这相当于说#{1}}在#1行'死'。这是在这里建立的正在重新分配foo
    2. 函数foo中对象new_foo的范围的生命周期不会延长函数func的生命周期。这也在这里建立为func的创建方式,它将在堆栈上,堆栈中对象的生命周期与创建对象的函数的生命周期相同。
    3. 在C ++中,可以使用内联函数new_foo来实现。内联后,代码基本上会是这样的。

      func

      尽管如此,C ++提供内联作为语言功能,但几乎所有优化编译器都会在最近进行内联。

      现在,这取决于您在`foo_t foo;` `foo_t new_foo;` `// operate on new_foo by using foo` `foo = new_foo;` new_foo上执行的操作类型是否会优化此额外foo。对于某些数据类型,它是微不足道的(编译器可以执行'复制传播',然后执行'死代码消除'以完全删除new_foo