如何有效地规范化向量 C++
How to efficiently normalize vector C++
我想知道如何在 C++ 中高效地规范化向量。到目前为止,这就是我所拥有的。有没有办法让它更有效率和/或一次性完成。
std::array<float, MyClass::FEATURE_LENGTH> MyClass::normalize(const std::array<float, FEATURE_LENGTH>& arr) {
std::array<float, MyClass::FEATURE_LENGTH> output{};
double mod = 0.0;
for (size_t i = 0; i < arr.size(); ++i) {
mod += arr[i] * arr[i];
}
double mag = std::sqrt(mod);
if (mag == 0) {
throw std::logic_error("The input vector is a zero vector");
}
for (size_t i = 0; i < arr.size(); ++i) {
output[i] = arr[i] / mag;
}
return output;
}
你怎么能一次搞定。很明显您需要使用所有项目计算 mag
并且您必须在更新项目之前计算它?
由于除法可能比乘法更费时,一种可能的优化是添加:
double mag_inv = 1.0 / mag;
然后你可以乘以这样的项目:
output[i] = arr[i] * mag_inv;
如果向量已经归一化的概率相对较高,您可能需要检查 mag
是否等于 1.0。
有很多方法可以优化此算法的实现,具体取决于您的问题的具体情况。
- 对于所有循环,您可以使用 SIMD 矢量化来增加吞吐量。
- 如果您的矢量 非常 宽,那么您可以使用多线程来计算大小。每个人都会计算一个部分和,然后一些串行代码会收集结果。
- 如果您的值在范围内,您可以完全使用浮点数而不是双精度值。
- 您可以使用内在函数计算幅度的平方根倒数(例如 RSQRTSS on x86) or using Quake's method,如果此类内在函数不可用。那么您将按该值缩放。
此外,通过将操作与规范化融合,您可以获得 多 更快的代码。假设您要添加两个向量并对结果进行归一化。您可以一次计算出它们的总和和大小,然后在一秒钟内进行缩放。
以防万一,如果有人需要这里是 SIMD 矢量化代码的示例:
#include <immintrin.h> //header for SIMD functions
void Normalize(const float lpInput[4], float lpOutput[4]) {
__m128 vInput = _mm_load_ps(lpInput); // load input vector (x, y, z, a)
__m128 vSquared = _mm_mul_ps(vInput, vInput); // square the input values
__m128 vHalfSum = _mm_hadd_ps(vSquared, vSquared);
__m128 vSum = _mm_hadd_ps(vHalfSum, vHalfSum); // compute the sum of values
float fInvSqrt; _mm_store_ss(&fInvSqrt, _mm_rsqrt_ss(vSum)); // compute the inverse sqrt
__m128 vNormalized = _mm_mul_ps(vInput, _mm_set1_ps(fInvSqrt)); // normalize the input vector
_mm_store_ps(lpOutput, vNormalized); // store normalized vector (x, y, z, a)
}
为了正确编译它,您需要在编译器选项中启用 SSE 和 AVX 指令(-msse -mavx for gcc 或 clang || /arch:sse /arch:avx for msvc)
我想知道如何在 C++ 中高效地规范化向量。到目前为止,这就是我所拥有的。有没有办法让它更有效率和/或一次性完成。
std::array<float, MyClass::FEATURE_LENGTH> MyClass::normalize(const std::array<float, FEATURE_LENGTH>& arr) {
std::array<float, MyClass::FEATURE_LENGTH> output{};
double mod = 0.0;
for (size_t i = 0; i < arr.size(); ++i) {
mod += arr[i] * arr[i];
}
double mag = std::sqrt(mod);
if (mag == 0) {
throw std::logic_error("The input vector is a zero vector");
}
for (size_t i = 0; i < arr.size(); ++i) {
output[i] = arr[i] / mag;
}
return output;
}
你怎么能一次搞定。很明显您需要使用所有项目计算 mag
并且您必须在更新项目之前计算它?
由于除法可能比乘法更费时,一种可能的优化是添加:
double mag_inv = 1.0 / mag;
然后你可以乘以这样的项目:
output[i] = arr[i] * mag_inv;
如果向量已经归一化的概率相对较高,您可能需要检查 mag
是否等于 1.0。
有很多方法可以优化此算法的实现,具体取决于您的问题的具体情况。
- 对于所有循环,您可以使用 SIMD 矢量化来增加吞吐量。
- 如果您的矢量 非常 宽,那么您可以使用多线程来计算大小。每个人都会计算一个部分和,然后一些串行代码会收集结果。
- 如果您的值在范围内,您可以完全使用浮点数而不是双精度值。
- 您可以使用内在函数计算幅度的平方根倒数(例如 RSQRTSS on x86) or using Quake's method,如果此类内在函数不可用。那么您将按该值缩放。
此外,通过将操作与规范化融合,您可以获得 多 更快的代码。假设您要添加两个向量并对结果进行归一化。您可以一次计算出它们的总和和大小,然后在一秒钟内进行缩放。
以防万一,如果有人需要这里是 SIMD 矢量化代码的示例:
#include <immintrin.h> //header for SIMD functions
void Normalize(const float lpInput[4], float lpOutput[4]) {
__m128 vInput = _mm_load_ps(lpInput); // load input vector (x, y, z, a)
__m128 vSquared = _mm_mul_ps(vInput, vInput); // square the input values
__m128 vHalfSum = _mm_hadd_ps(vSquared, vSquared);
__m128 vSum = _mm_hadd_ps(vHalfSum, vHalfSum); // compute the sum of values
float fInvSqrt; _mm_store_ss(&fInvSqrt, _mm_rsqrt_ss(vSum)); // compute the inverse sqrt
__m128 vNormalized = _mm_mul_ps(vInput, _mm_set1_ps(fInvSqrt)); // normalize the input vector
_mm_store_ps(lpOutput, vNormalized); // store normalized vector (x, y, z, a)
}
为了正确编译它,您需要在编译器选项中启用 SSE 和 AVX 指令(-msse -mavx for gcc 或 clang || /arch:sse /arch:avx for msvc)