如何使用 SSE 优化矩阵 3 乘 3 的点?
How to optimize a matrix 3 by 3 multiplication with a point with SSE?
我必须在我的图像的每个点上应用一个变换矩阵来获得新的点坐标。
为此,我创建了一个自定义 Matrix3by3
class,其中包含一个大小为 9 的浮点数组。
为了将矩阵应用于每个点,我首先创建了这个函数:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
const auto x = m.at(0, 0) * p.x + m.at(0, 1) * p.y + m.at(0, 2);
const auto y = m.at(1, 0) * p.x + m.at(1, 1) * p.y + m.at(1, 2);
const auto z = m.at(2, 0) * p.x + m.at(2, 1) * p.y + m.at(2, 2);
return { x / z, y / z };
}
如您所见,由于我的 2D 图像中没有 z 值,此函数将执行简单的矩阵乘法而不进行最后一次乘法。
这很好用,但由于这部分代码是热代码,我正在尝试优化它,所以我创建了它的 SSE 版本:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
using SSEVec3 = union {
struct
{
float z, y, x;
};
__m128 values_ = _mm_setzero_ps();
};
const auto mvec1 = _mm_set_ps(0, m.at(0, 0), m.at(0, 1), m.at(0, 2));
const auto mvec2 = _mm_set_ps(0, m.at(1, 0), m.at(1, 1), m.at(1, 2));
const auto mvec3 = _mm_set_ps(0, m.at(2, 0), m.at(2, 1), m.at(2, 2));
const auto pvec1 = _mm_set1_ps(static_cast<float>(p.x));
const auto pvec2 = _mm_set1_ps(static_cast<float>(p.y));
auto result = SSEVec3{};
result.values_ = _mm_add_ps(_mm_add_ps(_mm_mul_ps(mvec1, pvec1), _mm_mul_ps(mvec2, pvec2)), mvec3);
return { result.x / result.z, result.y / result.z };
}
这个也可以,但是比第一个版本慢,而且由于我正在学习 SSE,所以我不明白为什么会这样。
我对第二个版本的想法是并行计算 x、y 和 z 值。
所以,这就是我的问题,为什么 SSE 版本比较慢,我如何优化它以使其尽可能快?
谢谢!
一般来说,只优化需要优化的部分,而不是你认为需要优化的部分。
可能是(原始)代码中最糟糕的一点,而您的 'optimizations' 根本没有帮助,就是重复的 部分 。除浮点数或双精度数远比此代码中的其他所有内容都差,因此您最好的优化是通过计算 1/z(将 一次)计算为辅助变量,然后乘以来减少它两次结果。
但是 - 如开头所说 - 您可能不需要任何优化,或者您可能需要其他优化。测试、剖析并寻找最慢的一段代码。猜测通常会导致浪费精力和不必要的代码复杂性。
我必须在我的图像的每个点上应用一个变换矩阵来获得新的点坐标。
为此,我创建了一个自定义 Matrix3by3
class,其中包含一个大小为 9 的浮点数组。
为了将矩阵应用于每个点,我首先创建了这个函数:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
const auto x = m.at(0, 0) * p.x + m.at(0, 1) * p.y + m.at(0, 2);
const auto y = m.at(1, 0) * p.x + m.at(1, 1) * p.y + m.at(1, 2);
const auto z = m.at(2, 0) * p.x + m.at(2, 1) * p.y + m.at(2, 2);
return { x / z, y / z };
}
如您所见,由于我的 2D 图像中没有 z 值,此函数将执行简单的矩阵乘法而不进行最后一次乘法。
这很好用,但由于这部分代码是热代码,我正在尝试优化它,所以我创建了它的 SSE 版本:
constexpr auto apply_matrix(const Matrix3by3 & m, const Vec2i & p) -> Vec2f
{
using SSEVec3 = union {
struct
{
float z, y, x;
};
__m128 values_ = _mm_setzero_ps();
};
const auto mvec1 = _mm_set_ps(0, m.at(0, 0), m.at(0, 1), m.at(0, 2));
const auto mvec2 = _mm_set_ps(0, m.at(1, 0), m.at(1, 1), m.at(1, 2));
const auto mvec3 = _mm_set_ps(0, m.at(2, 0), m.at(2, 1), m.at(2, 2));
const auto pvec1 = _mm_set1_ps(static_cast<float>(p.x));
const auto pvec2 = _mm_set1_ps(static_cast<float>(p.y));
auto result = SSEVec3{};
result.values_ = _mm_add_ps(_mm_add_ps(_mm_mul_ps(mvec1, pvec1), _mm_mul_ps(mvec2, pvec2)), mvec3);
return { result.x / result.z, result.y / result.z };
}
这个也可以,但是比第一个版本慢,而且由于我正在学习 SSE,所以我不明白为什么会这样。
我对第二个版本的想法是并行计算 x、y 和 z 值。
所以,这就是我的问题,为什么 SSE 版本比较慢,我如何优化它以使其尽可能快?
谢谢!
一般来说,只优化需要优化的部分,而不是你认为需要优化的部分。
可能是(原始)代码中最糟糕的一点,而您的 'optimizations' 根本没有帮助,就是重复的 部分 。除浮点数或双精度数远比此代码中的其他所有内容都差,因此您最好的优化是通过计算 1/z(将 一次)计算为辅助变量,然后乘以来减少它两次结果。
但是 - 如开头所说 - 您可能不需要任何优化,或者您可能需要其他优化。测试、剖析并寻找最慢的一段代码。猜测通常会导致浪费精力和不必要的代码复杂性。