C++ 优化可变与不变向量数学

C++ 优化可变与不变向量数学,c++,vectorization,compiler-optimization,temporary-objects,C++,Vectorization,Compiler Optimization,Temporary Objects,哪种编码风格更适合编译器优化?特别是,我感兴趣的是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; }

哪种编码风格更适合编译器优化?特别是,我感兴趣的是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);
#为每个定义(int i=0;i
此结构的示例用法:

#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);
向量v1=。。。; 向量v2=。。。; v1.量表(10); v1.添加(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];
};
模板
结构向量{
向量(常量向量和其他){
memcpy(v,other.v,sizeof(v));
}
向量运算符+(常量向量和其他)常量{
矢量结果(*此);
对于_,每个结果.v[i]+=其他.v[i];
返回结果;
}
向量算子*(T标量)常数{
矢量结果(*此);
对于_,每个结果.v[i]*=标量;
返回结果;
}
向量运算符*(常量向量和其他)常量{
矢量结果(*此);
对于_,每个结果.v[i]*=其他.v[i];
返回结果;
}
tv[N];
};
用法示例:

Vector<int, 3> v1 = ...;
Vector<int, 3> v2 = ...;
auto result = (v1 * 10 + v2) * v2;
向量v1=。。。; 向量v2=。。。; 自动结果=(v1*10+v2)*v2;
现在,我不关心这个问题中的API设计。假设这两种解决方案在这方面都是可行的

此外,在示例代码中,它可以是
float
double
而不是
int

我感兴趣的是:哪种设计可以更容易地被现代C++编译器分析?我不是特别针对任何一个编译器。如果你有任何编译器的经验,并且知道它是如何处理优化的,请分享你的经验

  • 第二个版本生成许多临时值。如果编译器最终内联所有运算符调用并查看其中包含的所有算术表达式,那么它是否可以消除这些问题?(我假设没有内联,没有编译器可以消除临时性,因为可能会有副作用)

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

  • 对于现代编译器来说,将上述循环矢量化有多困难


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

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

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

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

有用链接:


元素的直接索引使编译器更容易对其进行矢量化。当使用复杂算法间接应用索引时,编译器可能会失败。感谢链接。我仍然对问题的另一部分感到好奇,那就是优化临时变量。我想我应该比较不同编译器的输出来确定。在这里检查我的回答,可能会有所帮助。