c++ - 针对编译时常量优化的函数

标签 c++ c++17 c++20

假设我有一个 vector 长度计算函数,它有一个额外的 inc 参数(这告诉相邻元素之间的距离)。一个简单的实现是:

float calcLength(const float *v, int size, int inc) {
    float l = 0;

    for (int i=0; i<size*inc; i += inc) {
        l += v[i]*v[i];
    }
    return sqrt(l);
}

现在,calcLength 可以用两种 inc 参数调用:当 inc 在编译时已知时,当它是不是。我想为 inc(如 1)的常见编译时值优化 calcLength 版本。

所以,我会有这样的东西:

template <int C>
struct Constant {
    static constexpr int value() {
        return C;
    }
};

struct Var {
    int v;

    constexpr Var(int p_v) : v(p_v) { }

    constexpr int value() const {
        return v;
    }
};

template <typename INC>
float calcLength(const float *v, int size, INC inc) {
        float l = 0;

        for (int i=0; i<size*inc.value(); i += inc.value()) {
            l += v[i]*v[i];
        }
        return sqrt(l);
    }
}

因此,可以使用:

calcLength(v, size, Constant<1>()); // inc is a compile-time constant 1 here, calcLength can be vectorized

int inc = <some_value>;
calcLength(v, size, Var(inc)); // inc is a non-compile-time constant here, less possibilities of compiler optimization

我的问题是,是否有可能以某种方式保留原始接口(interface),并根据类型(编译时常量或不是)inc

calcLength(v, size, 1); // this should end up calcLength(v, size, Constant<1>());
calcLength(v, size, inc); // this should end up calcLength(v, size, Var(int));

注意:这是一个简单的例子。在我的实际问题中,我有几个函数,如calcLength,而且它们很大,我不希望编译器内联它们。


注意 2:我也对不同的方法持开放态度。基本上,我想要一个解决方案来满足这些:

  • 算法只指定一次(很可能在模板函数中)
  • 如果我将 1 指定为 inc,则会实例化一个特殊函数,并且代码很可能会被矢量化
  • 如果inc不是编译时常量,则调用一个通用函数
  • 否则(非 1 编译时常量):调用哪个函数无关紧要

最佳答案

如果这里的目标只是优化,而不是在编译时上下文中使用,您可以向编译器提示您的意图:

static float calcLength_inner(const float *v, int size, int inc) {
    float l = 0;

    for (int i=0; i<size*inc; i += inc) {
        l += v[i]*v[i];
    }
    return sqrt(l);
}

float calcLength(const float *v, int size, int inc) {
    if (inc == 1) {
        return calcLength_inner(v, size, inc);  // compiler knows inc == 1 here, and will optimize
    }
    else {
        return calcLength_inner(v, size, inc);
    }
}

From godbolt ,您可以看到 calcLength_inner 已被实例化两次,有和没有常量传播。

这是一个 C 技巧(在 numpy 中广泛使用),但您可以编写一个简单的包装器以使其更易于在 C++ 中使用:

// give the compiler a hint that it can optimize `f` with knowledge of `cond`
template<typename Func>
auto optimize_for(bool cond, Func&& f) {
    if (cond) {
        return std::forward<Func>(f)();
    }
    else {
        return std::forward<Func>(f)();
    }
}

float calcLength(const float *v, int size, int inc) {
    return optimize_for(inc == 1, [&]{
        float l = 0;
        for (int i=0; i<size*inc; i += inc) {
            l += v[i]*v[i];
        }
        return sqrt(l);
    });
}

关于c++ - 针对编译时常量优化的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55248789/

相关文章:

c++ - 我们可以在 c++20 协程中使用 alloca() 或可变长度数组扩展吗?

c++ - 我可以在C++ 20的类型别名中使用条件吗?

c++ - 在 C++ 中嵌入 Mongoose Web 服务器

c++ - 默认构造函数参数

c++ - 在 ubuntu 上安装 npm 期间,使用 C++ 17 编译 native Node 插件失败

c++ - 输入特征样式来测试 T 是否是 basic_string<> 或可打印的类似字符串类型?

c++ - 如何检查内存块中的所有字节是否为零

java - 如何在 JNI 中将 char* 转换为 jcharArray

c++ - 可变参数模板与使用元组在参数中添加不同的数据对

c++ - SFINAE 自动检查函数体是否在没有明确约束的情况下编译