如何有效地规范化向量 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。

有很多方法可以优化此算法的实现,具体取决于您的问题的具体情况。

  1. 对于所有循环,您可以使用 SIMD 矢量化来增加吞吐量。
  2. 如果您的矢量 非常 宽,那么您可以使用多线程来计算大小。每个人都会计算一个部分和,然后一些串行代码会收集结果。
  3. 如果您的值在范围内,您可以完全使用浮点数而不是双精度值。
  4. 您可以使用内在函数计算幅度的平方根倒数(例如 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)