同时将所有结构元素与标量相乘
Simultaneously multiply all struct-elements with a scalar
我有一个表示向量的结构。该向量由两个单字节整数组成。我用它们来保持 0 到 255 之间的值。
typedef uint8_T unsigned char;
struct Vector
{
uint8_T x;
uint8_T y;
};
现在,我程序中的主要用例是将向量的两个元素与 32 位浮点值相乘:
typedef real32_T float;
Vector Vector::operator * ( const real32_T f ) const {
return Vector( (uint8_T)(x * f), (uint8_T)(y * f) );
};
这需要经常执行。有没有办法可以同时执行这两个乘法?也许通过矢量化、SSE 或类似方法?或者 Visual studio 编译器是否已经同时执行此操作?
另一个用例是在两个向量之间进行插值。
Vector Vector::interpolate(const Vector& rhs, real32_T z) const
{
return Vector(
(uint8_T)(x + z * (rhs.x - x)),
(uint8_T)(y + z * (rhs.y - y))
);
}
这已经使用了优化的插值方法 ()。
但是向量的值再次乘以相同的标量值。
是否有可能提高这些操作的性能?
谢谢
(我使用 Visual Studio 2010 和 64 位编译器)
根据我的经验,Visual Studio(尤其是像 VS2010 这样的旧版本)本身并没有做很多向量化。他们在较新的版本中对其进行了改进,因此如果可以的话,您可能会看到更改编译器是否可以加速您的代码。
根据使用这些函数的代码和编译器所做的优化,减慢程序速度的甚至可能不是计算。函数调用和缓存未命中可能会造成更多伤害。
您可以尝试以下方法:
- 如果还没有完成,请在头文件中定义函数,以便编译器可以内联它们
- 如果您在紧密循环中使用这些函数,请尝试在不调用任何函数的情况下进行计算 'by hand'(暂时公开变量),看看速度是否有所不同)
- 如果你有很多向量,看看它们在内存中的布局。连续存储它们以最大程度地减少缓存未命中。
- 要使 SSE 真正发挥作用,您必须同时处理 4 个值 - 因此将 2 个向量乘以 2 个浮点数。在一个循环中,使用步长 2 并编写一个静态函数,使用 SSE 指令一次计算 2 个向量。因为您的向量未对齐(并且几乎不会与 8 位变量对齐),代码甚至可能 运行 比您现在拥有的慢,但值得一试。
- 如果适用并且您不依赖于从
float
到 uint8_t
的强制转换(例如,如果您的浮点数在 [0,1] 范围内),请尝试到处使用 float
。这可能会让编译器做更好的优化。
你没有展示完整的算法,但是整数和浮点数之间的转换是一个非常慢的操作。消除此操作并仅使用一种类型(如果可能最好是整数)可以大大提高性能。
或者,您可以使用 lrint()
进行转换,如 here 所述。
我有一个表示向量的结构。该向量由两个单字节整数组成。我用它们来保持 0 到 255 之间的值。
typedef uint8_T unsigned char;
struct Vector
{
uint8_T x;
uint8_T y;
};
现在,我程序中的主要用例是将向量的两个元素与 32 位浮点值相乘:
typedef real32_T float;
Vector Vector::operator * ( const real32_T f ) const {
return Vector( (uint8_T)(x * f), (uint8_T)(y * f) );
};
这需要经常执行。有没有办法可以同时执行这两个乘法?也许通过矢量化、SSE 或类似方法?或者 Visual studio 编译器是否已经同时执行此操作?
另一个用例是在两个向量之间进行插值。
Vector Vector::interpolate(const Vector& rhs, real32_T z) const
{
return Vector(
(uint8_T)(x + z * (rhs.x - x)),
(uint8_T)(y + z * (rhs.y - y))
);
}
这已经使用了优化的插值方法 ()。
但是向量的值再次乘以相同的标量值。 是否有可能提高这些操作的性能?
谢谢
(我使用 Visual Studio 2010 和 64 位编译器)
根据我的经验,Visual Studio(尤其是像 VS2010 这样的旧版本)本身并没有做很多向量化。他们在较新的版本中对其进行了改进,因此如果可以的话,您可能会看到更改编译器是否可以加速您的代码。
根据使用这些函数的代码和编译器所做的优化,减慢程序速度的甚至可能不是计算。函数调用和缓存未命中可能会造成更多伤害。
您可以尝试以下方法:
- 如果还没有完成,请在头文件中定义函数,以便编译器可以内联它们
- 如果您在紧密循环中使用这些函数,请尝试在不调用任何函数的情况下进行计算 'by hand'(暂时公开变量),看看速度是否有所不同)
- 如果你有很多向量,看看它们在内存中的布局。连续存储它们以最大程度地减少缓存未命中。
- 要使 SSE 真正发挥作用,您必须同时处理 4 个值 - 因此将 2 个向量乘以 2 个浮点数。在一个循环中,使用步长 2 并编写一个静态函数,使用 SSE 指令一次计算 2 个向量。因为您的向量未对齐(并且几乎不会与 8 位变量对齐),代码甚至可能 运行 比您现在拥有的慢,但值得一试。
- 如果适用并且您不依赖于从
float
到uint8_t
的强制转换(例如,如果您的浮点数在 [0,1] 范围内),请尝试到处使用float
。这可能会让编译器做更好的优化。
你没有展示完整的算法,但是整数和浮点数之间的转换是一个非常慢的操作。消除此操作并仅使用一种类型(如果可能最好是整数)可以大大提高性能。
或者,您可以使用 lrint()
进行转换,如 here 所述。