优化的范围检查并返回值
Optimised range checking and returning a value
>我有以下函数,它根据输入返回一个值。我需要尽可能快地编写此代码,而不使用除法或模运算符或循环。每个连续值之间的数量几乎等于 6553。
int GetScalingFactor(int input)
{
unsigned int factor = 0;
if(input < 13107) factor = 72816;
else if(input < 19660) factor = 81918;
else if(input < 26214) factor = 93621;
else if(input < 32767) factor = 109225;
else if(input < 39321) factor = 131070;
else if(input < 45874) factor = 163837;
else if(input < 52428) factor = 218450;
else if(input < 58981) factor = 327675;
return factor;
}
在 c++ 中使用 std::lower_bound
:
int GetScalingFactor(int input)
{
const unsigned int inputs[] = {13107, 19660, 26214, 32767, 39321, 45874, 52428, 58981};
const int factors[] = {72816, 81918, 93621, 109225, 131070, 163837, 218450, 327675, 0};
auto it = std::lower_bound(std::begin(inputs), std::end(inputs), input + 1);
return factors[std::distance(std::begin(inputs), it)];
}
演示
您可以准备一个包含重复72816
13107
次、81918
重复19660-13107
次等的表,然后只检查上限 (58981(。如果在范围内,只需返回table[input]
否则返回0
,就像您当前(应该(所做的那样。
除法,没有模数,只有一些分配的内存(远低于 1 兆字节(和预先计算的表。
概念验证:
#include <stdio.h>
#include <stdint.h>
int32_t table[58981];
void prepare_table()
{
int32_t input,factor;
for (input=0;input<sizeof(table)/sizeof(table[0]);input++)
{
// just reusing your code as-is, but only to create the table
if(input < 13107) factor = 72816;
else if(input < 19660) factor = 81918;
else if(input < 26214) factor = 93621;
else if(input < 32767) factor = 109225;
else if(input < 39321) factor = 131070;
else if(input < 45874) factor = 163837;
else if(input < 52428) factor = 218450;
else if(input < 58981) factor = 327675;
table[input] = factor;
}
}
int GetScalingFactor(int input)
{
return input < sizeof(table)/sizeof(table[0]) ? table[input] : 0;
}
int main() {
prepare_table();
printf("%d => %dn",19600,GetScalingFactor(19600));
printf("%d => %dn",26200,GetScalingFactor(26200));
printf("%d => %dn",58000,GetScalingFactor(58000));
printf("%d => %dn",60000,GetScalingFactor(60000));
}
所以这是内存与计算的权衡。如果您无法承受缓存未命中,那么除了除法或多个测试之外,您别无选择。
当我们可以使用模板扩展在编译时计算二叉搜索时,为什么要使用数据?
概要:此代码为每个索引序列生成自定义lower_bound实现。
前提条件:每个索引必须按升序出现在元组中。
结果:在 clang 3.9.1 上,未生成输入数组。编译器只是以最有效的顺序比较每个绑定。GCC 决定创建一个数组并有效地实现lower_bound本身(哇!
法典:
#include <utility>
#include <tuple>
// turn values into types
template<std::size_t I> using index = std::integral_constant<std::size_t, I>;
// termination case
template<class T, class Tuple, std::size_t it>
std::size_t iteration(T value, Tuple&&, index<it>, index<0>)
{
return it;
}
// end of search 'else' path which will not be taken but there must
// be code available at compile time
template<class T, class Tuple, std::size_t first, std::size_t count, std::enable_if_t<(first >= count)>* = nullptr>
std::size_t iteration(T value, Tuple&& tuple, index<first>, index<count>)
{
return count-1;
}
// normal iteration of the lower_bound loop
template<class T, class Tuple, std::size_t first, std::size_t count, std::enable_if_t<(first < count)>* = nullptr>
std::size_t iteration(T value, Tuple&& tuple, index<first>, index<count>)
{
constexpr auto step = count / 2;
constexpr auto it = first + step;
if(std::get<it>(tuple) < value)
{
return iteration(value, std::forward<Tuple>(tuple), index<it>(), index<step + 1>());
}
else {
return iteration(value, std::forward<Tuple>(tuple), index<first>(), index<step>());
}
}
// expand out a lower-bound algorithm from a tuple of bounds
template<class Tuple, class T>
constexpr std::size_t tuple_lower_bound(Tuple&& tuple, const T& value)
{
constexpr auto count = index<std::tuple_size<std::decay_t<Tuple>>::value>();
constexpr auto first = index<0>();
return iteration(value, std::forward<Tuple>(tuple), first, count);
}
int GetScalingFactor(int input)
{
static constexpr auto indexes = std::make_tuple(13107, 19660, 26214, 32767, 39321, 45874, 52428, 58981);
static constexpr std::array<int, std::tuple_size<std::decay_t<decltype(indexes)>>::value + 1> factors =
{{
72816, 81918, 93621, 109225, 131070, 163837, 218450, 327675, 0
}};
auto i = tuple_lower_bound(indexes, input + 1);
return factors[i];
}
int main()
{
extern int get_input();
auto s1 = GetScalingFactor(get_input());
return s1;
}
局部变量因子不是很有用。
我认为你不能真正优化"如果否则"的序列,但你可以思考什么是更常见的情况,并首先测试它们。所以大多数时候只有第一个条件是过程。
int GetScalingFactor(int input) {
if(input < 13107) return 72816; // most common case
else if(input < 19660) return 81918; // second common case
else if(input < 26214) return 93621; // ...
else if(input < 32767) return 109225;
else if(input < 39321) return 131070;
else if(input < 45874) return 163837;
else if(input < 52428) return 218450;
else if(input < 58981) return 327675;
else return 0;
}
如果编译器实现常量的快速除法(使用乘法和移位(,则以下函数将起作用:
int GetScalingFactor(int input)
{
static const int factors[] =
{
72816, 72816, 81918, 93621, 109225, 131070, 163837, 218450, 327675
};
if (input < 0)
{
input = 0;
}
else
{
input = (input * 2 + 1) / 13107;
if (input >= sizeof(factors) / sizeof(factors[0]))
{
return 0;
}
}
return factors[input];
}
鉴于输入是完全任意的,并且没有比其他情况更有可能的情况,那么您可以将检查重写为硬编码的二进制搜索,其中您将搜索的数据间隔分成两部分,每个if语句:
if(input < 32767ul)
{
if(input < 19660ul)
{
...
}
...
}
else if(input < 45874ul)
{
...
}
依此类推(在编码之前将其绘制在纸上作为二叉搜索树,如果有帮助的话(。这减少了与"O log(n("的比较次数,并且是您可以实现的最佳效果,而无需创建包含 58981 个项目的巨大查找表,其中input
是索引 - 这将是执行速度方面性能最佳的解决方案。
此外,您的代码有错误,您不应该将无符号变量与int
混合。将数据类型切换为 uint_fast32_t
。
- 从python中调用C++函数并获取返回值
- 为什么模板类中的对象不能返回值
- 返回值优化:显式移动还是隐式
- lock_guard是否保护返回值
- 调用CreateProcess()并获取字符串的返回值
- 如何使 windows 命令提示符在C++可执行文件上显示返回值?
- 编译器警告:执行到达值返回函数的末尾而不返回值
- 查找 GCD:并非所有控制路径都返回值
- 在 Arduino 上使用 sscanf 会导致与 const char * 不匹配,并且返回值始终相同,尽管输入值不同
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 如何从 std::thread 返回值
- 检查 std::string.c_str() 的返回值
- 如何获取有关 Visual Studio 代码分析中未经检查的函数返回值的警告
- 优化的范围检查并返回值
- 如何避免使用多个if-else来检查返回值是否为错误代码?
- 有必要用C++检查发送0MQ的返回值吗
- 如何检查忍者并行构建的返回值
- 是否有任何 ATL 支持的宏可以检查 hResults 并返回值
- 在 C++ 中传递 int 变量 - 错误检查不返回值
- 检查返回值是整数还是字符