std::vector 的 C++ 钳位函数

C++ clamp function for a std::vector

void Clamp(std::vector<double>& input_image, double min_value, double max_value, int width, int height)
{
    for (int j = 0; j < width; j++)
    {
        for (int i = 0; i < height; i++)
        {
            input_image[i*width + j] = std::min(std::max(input_image[i*width + j], min_value), max_value);
        }
    }
}

我正在尝试创建此钳位功能。

使用 std::vector 有更好的方法吗?

我也可以用C++11

给你。我进行了两次转换只是为了让编译器有更好的机会进行优化和矢量化。如果您不同意,可以在 1 lambda 中加入他们。

std::transform(input_image.begin(), input_image.end(), input_image.begin(), [lo](double v) {return (v < lo ? lo : v);});
std::transform(input_image.begin(), input_image.end(), input_image.begin(), [hi](double v) {return (v > hi ? hi : v);});

永远记住循环连续的内存元素要快得多。请参阅 std::transform reference 以了解其工作原理。

您可以使用 std::transform,假设您将函数应用于每个元素,那么就不需要高度和宽度参数。拆分最小和最大操作似乎对 SIMD 更友好,因此我建议:

void Clamp(std::vector<double>& input_image, double min_value, double max_value)
{
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image), [max_value] (double d) { return std::min(d, max_value); }
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image), [min_value] (double d) { return std::max(d, max_value); }
}

否则 C++11 的方法可能是使用 range-based for loop:

void Clamp(std::vector<double>& input_image, double min_value, double max_value) // removed hight and width
{
    for (double& d : input_image)
    {
        d = std::min(std::max(d, min_value), max_value);
    }
}

或更多SIMD "friendly":

void Clamp(std::vector<double>& input_image, double min_value, double max_value) // removed height and width
{
    for (double& d : input_image)
    {
        d = std::max(d, min_value);
    }
    for (double& d : input_image)
    {
        d = std::min(d, max_value);
    }
}

C++17 有 std::clamp。在这种情况下也不需要 heightwidth 参数。

void clamp_vec(std::vector<double>& input_image, double min_value, double max_value) {
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image),
                   [=] (auto i) { return std::clamp(i, min_value, max_value); });
}

只要定义了运算符 <,通用类型的模板版本就可以工作:

template <typename T>
void clamp_vec(std::vector<T>& input_image, const T& min_value, const T& max_value) {
    std::transform(std::begin(input_image), std::end(input_image), std::begin(input_image),
                   [&] (const T& v) { return std::clamp(v, min_value, max_value); });
}

如果 std::clamp 不可用,您可以自己实现,直到 C++17 出现,例如

template <typename T>
constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
    return clamp(v, lo, hi, std::less<>());
}

template <typename T, typename Compare>
constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) {
    return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
}

我想我会一次完成,让优化器完成它的工作。

void Clamp(std::vector<double>& input_image, double min_value, double max_value)
{
    auto clamp = [min_value, max_value](double x) {
        return std::min(std::max(x, min_value), max_value);
    };

    std::transform(input_image.begin(), input_image.end(),
                   input_image.begin(), clamp);
}

在 godbolt 上对此进行测试,gcc 将其矢量化并完全消除了 clamp.

的冗余副本