优化可变与不可变向量数学
Optimizing mutable vs immutable vector math
哪种编码风格更适合编译器优化?特别是,我对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
,它也可以是float
或double
。
我感兴趣的是:现代C++编译器可以更容易地分析哪种设计?我不是特别针对任何一个编译器。如果你有任何编译器的经验,并且知道它是如何处理我所问的优化的,请分享你的经验。
第二个版本产生了很多临时值。如果编译器最终内联所有运算符调用并查看其中包含的所有算术表达式,那么它能去掉这些吗?(我假设没有内联,任何编译器都无法消除临时性,因为可能会产生副作用)
第一个版本最小化了临时性的数量,但构造了严格的顺序计算。编译器还能以最小化操作数量并允许其并行化(在CPU指令级别)的方式推断意图并重新排序操作吗?
对于现代编译器来说,将上面的循环向量化有多难?
据我所知,只要目标体系结构中有支持,第一个例子就很容易向量化。这是因为在连续迭代中元素之间不存在数据依赖性。
如果您有循环,其中在连续迭代中元素之间存在数据依赖关系,在某些情况下,可以通过软件管道删除这些依赖关系。软件流水线有助于矢量化。
在一些体系结构中,由于浮点执行单元有限,浮点计算不容易矢量化。
在第二个示例中,存在可以通过内联消除的临时性。
有用的链接:
- 矢量化以矢量格式编写cc代码
- 利用软件流水线循环中的矢量并行性
相关文章:
- 正在寻找C++不可变的hashset/hashmap
- 具有常量属性的不可变类
- 为什么向量不在代码块中运行?
- 为什么将函数传递给内核会导致数据变得不可变?
- 现代c++编译器会优化不可变的临时变量吗
- C++中的不可变列表
- 为什么向量不支持 size 作为成员变量,就像 Java 类中的长度变量一样,而是函数 size()?
- std::包含 std::函数回调的多个包装器的向量不起作用
- 如何将地图作为不可变地图传递?
- 不可变的全局对象应该声明为"const my_result_t BLAH"还是"extern const my_result_t BLAH;"?
- 向不可变纹理存储提供数据
- 在C++中创建不可变且高效的类的惯用方法
- std::shared_ptr<std::string const> 可以作为引用计数的不可变字符串的有效实现吗?
- 编写接受可变跨度作为不可变跨度的模板函数
- 可变存储与不可变存储
- 不可变数据模型的内存管理
- 在C++中创建不可变对象的更好方法
- C++返回不可变指针向量的函数语法
- 优化可变与不可变向量数学
- 不可变向量的不可变子向量