10的幂的表查找

Table lookups of Powers of 10

本文关键字:查找      更新时间:2023-10-16

我正在进行一个C++项目,在该项目中计算速度至关重要。任何我能剪的时候,都应该剪。可执行文件的最终大小和内存使用情况并不重要。

话虽如此,创建一个预先计算的10次方查找表是否有益?例如,如果我有一个简化的函数:

double powTen(int exponent) {
return pow(10, exponent);
}

如果我用表查找取代pow(),它的性能会有(小)提高吗?这值得吗?似乎pow()函数一定相当复杂。此外,GCC对此有特殊的优化吗?

表查找对于性能来说几乎总是很好的。因此,假设您已经确定这是一个瓶颈,我会将时间用于实现生成表的模板。但是,一定要测量。性能在现代计算机上是一种奇怪的野兽。它可以朝着人们永远不会相信的方向发展。

我预计实现编译时间表生成的时间确实很短。但事实证明,Visual C++2015,至少在旧的更新2中,对类中的constexpr std::array一点也不满意。然而,在最后,一个原始数组起了作用。

代码,使用MinGW g++6.3.0和Visual C++2015更新2:编译

#include <stddef.h>     // size_t, ptrdiff_t
#include <utility>      // std::(make_index_sequence, )
namespace my {
using std::make_index_sequence;
using std::index_sequence;
using Size = ptrdiff_t;
template< Size n, class Item >
using raw_array_of_ = Item[n];
template< class Item, size_t n >
constexpr
auto n_items_of( raw_array_of_<n, Item>& )
-> Size
{ return n; }
namespace impl {
constexpr
auto compile_time_pow_of_10( int const n )
-> double
{ return (n == 0? 1.0 : 10.0*compile_time_pow_of_10( n - 1 )); }
template< size_t... indices >
struct Powers_of_10_
{
static constexpr size_t                     n       = sizeof...(indices);
static constexpr raw_array_of_<n, double>   table   =
{
compile_time_pow_of_10( indices )...
};
constexpr
Powers_of_10_() {}
};
template< size_t... indices >
constexpr
raw_array_of_<Powers_of_10_<indices...>::n, double>
Powers_of_10_<indices...>::table;
template< size_t... indices, int n = sizeof...(indices) >
constexpr
auto power_of_10_table_helper( index_sequence<indices...> )
-> const raw_array_of_<n, double>&
{ return Powers_of_10_<indices...>::table; }
}  // namespace impl
template< int n >
constexpr
auto power_of_10_table()
-> const raw_array_of_<n, double>&
{ return impl::power_of_10_table_helper( make_index_sequence<n>() ); }
}  // namespace my
#include <iostream>
using namespace std;
auto main()
-> int
{
int const n = 7;
constexpr auto& pow_10 = my::power_of_10_table<n>();
cout << n << " powers of 10:n";
cout << fixed;  cout.precision( 0 );
for( int i = 0; i < n; ++i )
{
cout << pow_10[i] << "n";
}
}

模板代码可能很难移植。也就是说:Visual C++2015拒绝接受上述代码中的std::array,因此需要重写以使用原始数组。它也很难维护,因为通常编译诊断程序过长且晦涩难懂。

合理快速的积分幂函数可以通过将幂表示为形式为x(2n)的幂的乘积来定义。例如,x42=x32x8x2,并且x-的这些更基本的幂,即2、8和32,可以通过反复对x-求平方来计算。这将乘法运算的次数从指数中的线性减少到指数中的对数。

代码:

#include <stddef.h>     // size_t, ptrdiff_t
namespace my {
using Size = ptrdiff_t;
template< Size n, class Item >
using raw_array_of_ = Item[n];
namespace impl {
auto positive_integral_power_of( const double x, const int n )
{
double result = 1.0;
double power = x;
for( unsigned exp = n; ; )
{
if( (exp & 1) != 0 )
{
result *= power;
}
exp >>= 1;
if( exp == 0 )
{
break;
}
power *= power;
}
return result;
}
}  // namespace impl
auto integral_power_of( const double x, const int n )
-> double
{
return
n > 0? 
impl::positive_integral_power_of( x, n ) :
n < 0?
1.0 / impl::positive_integral_power_of( x, -n ) :
1.0;
}
}  // namespace my
#include <iostream>
using namespace std;
auto main()
-> int
{
int const n = 7;
cout << n << " powers of 10:n";
cout << fixed;  cout.precision( 0 );
for( int i = 0; i < n; ++i )
{
cout << my::integral_power_of( 10, i ) << "n";
}
}

您可以通过使用循环来提高函数的效率:

double powerTen(int exponent)
{
double result = 1.0;
for (int i = 0; i < exponent; ++i)
{
result = result * 10.0;
}
return result;
}

循环的一个好处是可以消除pow函数的函数调用开销。节省不了多少钱,IMO.

另一种选择是表格查找:

double powerTen(int exponent)
{
static const double values[] = {1.0, 10.0, 100.0, 1000.0};
return values[exponent];
}

您正在用内存空间换取执行时间
此外,您可能希望添加一些数组溢出检查以及处理负指数。

你调用这个函数那么多吗?如果是这样,那么预计算期望值会更好。

您也可以使用部分构建的表,并根据使用情况进行构建,即第一次调用10^5时计算它,然后保存在表中,以便下次从表中获取它。

使用表总是比调用函数好,但问题是它必须首先值得,通过多次调用或多次发送相同的参数,这样你就不会在第一个大时间调用函数,然后根本不使用,这会破坏你优化的初衷。