【问题标题】:Optimizing mutable vs immutable vector math优化可变与不可变向量数学
【发布时间】:2013-07-12 11:43:22
【问题描述】:

哪种编码风格更适合编译器优化?特别是,我对 1) 最小化立即丢弃的临时值的数量和 2) 自动矢量化(即生成用于算术的 SIMD 指令)感兴趣。

假设我有这个结构:

#define FOR_EACH for (int i = 0; i < N; ++i)

template<typename T, unsigned N>
struct Vector {
    void scale(T scalar) {
        FOR_EACH v[i] *= scalar;
    }

    void add(const Vector<T, N>& other) {
        FOR_EACH v[i] += other.v[i];
    }

    void mul(const Vector<T, N>& other) {
        FOR_EACH v[i] *= other.v[i];
    }

    T v[N];
};

此结构的示例用法:

Vector<int, 3> v1 = ...;
Vector<int, 3> v2 = ...;
v1.scale(10);
v1.add(v2);
v1.mul(v2);

这是可变的方法。

另一种不可变方法可能如下所示:

template<typename T, unsigned N>
struct Vector {
    Vector(const Vector<T, N>& other) {
        memcpy(v, other.v, sizeof(v));
    }

    Vector<T, N> operator+(const Vector<T, N>& other) const {
        Vector<T, N> result(*this);
        FOR_EACH result.v[i] += other.v[i];
        return result;
    }

    Vector<T, N> operator*(T scalar) const {
        Vector<T, N> result(*this);
        FOR_EACH result.v[i] *= scalar;
        return result;
    }

    Vector<T, N> operator*(const Vector<T, N>& other) const {
        Vector<T, N> result(*this);
        FOR_EACH result.v[i] *= other.v[i];
        return result;
    }

    T v[N];
};

示例用法:

Vector<int, 3> v1 = ...;
Vector<int, 3> v2 = ...;
auto result = (v1 * 10 + v2) * v2;

现在,我不关心这个问题中的 API 设计。假设这两种解决方案在这方面都是可行的。

此外,示例代码中的int 也可以是floatdouble

我感兴趣的是:现代 C++ 编译器更容易分析哪种设计?我并不是特别针对任何单个编译器。如果您有使用任何编译器的经验并且知道它如何处理我所询问的优化,请分享您的经验。

  • 第二个版本产生了很多临时值。如果编译器最终内联所有运算符调用并看到其中包含的所有算术表达式,它能否摆脱这些? (我假设没有内联没有编译器可以消除临时因为可能的副作用)

  • 第一个版本最大限度地减少了临时变量的数量,但构造了严格的顺序计算。编译器是否仍然可以推断出意图并以最小化操作数量并允许并行化(在 CPU 指令级别)的方式对操作重新排序?

  • 现代编译器对上述循环进行矢量化有多难?

【问题讨论】:

  • 元素的直接索引使编译器更容易向量化它们。当索引间接应用于复杂算法时,编译器可能会失败。

标签: c++ vectorization compiler-optimization temporary-objects


【解决方案1】:

据我了解,只要目标架构有支持,第一个示例很容易矢量化。这是因为在连续迭代中元素之间没有数据依赖关系。

如果您有循环,其中在连续迭代中元素之间存在数据依赖关系,在某些情况下,它们可以通过软件流水线删除。软件流水线有助于矢量化。

在某些架构中,由于浮点执行单元有限,浮点计算不容易矢量化。

在第二个示例中,存在可以通过内联消除的临时变量。

有用的链接:

【讨论】:

  • 感谢您的链接。我仍然对问题的另一部分感到好奇,即优化临时工。我想我应该只是比较不同编译器的输出来确定。
  • 在这里查看我的回复stackoverflow.com/a/17476463/811335,它可能会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多