【问题标题】:lookup table vs runtime computation efficiency - C++查找表与运行时计算效率 - C++
【发布时间】:2013-03-16 17:40:32
【问题描述】:

我的代码需要不断地从以下函数计算一个值:

inline double f (double x) {
    return ( tanh( 3*(5-x)  ) *0.5 + 0.5);
}

分析表明程序的这一部分是花费大部分时间的地方。由于该程序将运行数周甚至数月,因此我想优化此操作并考虑使用查找表。

我知道查找表的效率取决于表本身的大小以及它的设计方式。目前我不能使用少于 100 MB 的空间,最多可以使用 2GB。矩阵中两点之间的值将被线性插值。

使用查找表会比计算更快吗?此外,使用 N 维矩阵会比一维 std::vector 更好吗?不应该跨越的表大小的阈值(如果有)是多少?

【问题讨论】:

  • 即使该功能是花费大部分时间的地方,是否会损害程序的其余部分?使用表格和外推可能会更快,但值得您的时间进行此优化吗?它会帮助使用您的程序的人吗?请记住,除非使用您的程序的人开始抱怨它运行缓慢,否则您真的需要它吗?
  • 我认为,如果您向我们展示相关代码(最好是作为 SSCCE 的一部分 - sscce.org),您将更有可能获得高质量的帮助。否则 IMO 这个问题有点太抽象了。
  • 我不能使用少于 10 MB 的空间 - 这是您的老师对您使用查找表的限制吗?这似乎很奇怪。
  • 如果您有巨大的查找表(如您所说的数百 MB),它不适合缓存 - 很可能内存查找时间会比计算本身高得多。 RAM“非常慢”。
  • tanh of 15 - 3x 分别达到 1 和 -1 的水平渐近线大约 1 和 9。你真的只需要为x的一小部分计算它。

标签: c++ performance caching optimization lookup-tables


【解决方案1】:

我正在编写一个代码,该代码不断需要从特定函数中计算一个值。经过一些分析,我发现我的程序的这一部分是花费大部分时间的地方。

到目前为止,我不能使用少于 100 MB 的空间,最多可以使用 2 GB。线性插值将用于矩阵中点之间的点。

如果您有巨大的查找表(如您所说的数百 MB),它不适合缓存 - 很可能内存查找时间将远高于计算本身。 RAM“非常慢”,尤其是在从巨大数组的随机位置获取时。

这是综合测试:

live demo

#include <boost/progress.hpp>
#include <iostream>
#include <ostream>
#include <vector>
#include <cmath>

using namespace boost;
using namespace std;

inline double calc(double x)
{
    return ( tanh( 3*(5-x)  ) *0.5 + 0.5);
}

template<typename F>
void test(F &&f)
{
   progress_timer t;
   volatile double res;
   for(unsigned i=0;i!=1<<26;++i)
      res = f(i);
   (void)res;
}

int main()
{
   const unsigned size = (1 << 26) + 1;
   vector<double> table(size);
   cout << "table size is " << 1.0*sizeof(double)*size/(1 << 20) << "MiB" << endl;
   cout << "calc ";
   test(calc);
   cout << "dummy lookup ";
   test([&](unsigned i){return table[(i << 12)%size];}); // dummy lookup, not real values
}

我机器上的输出是:

table size is 512MiB
calc 0.52 s

dummy lookup 0.92 s

【讨论】:

  • 谢谢。我的情况可能与此略有不同,但这个例子可能会为我指明正确的道路。我自己没有尝试的原因是还有其他我没有提到的问题,并且测试需要一些工作。再次感谢:)
  • 至少在这个测试套件/架构中,有一个有趣的跳跃,0.25MiB-2MiB 是 ~0.59s,但 4MiB 是 ~1.20s,8MiB-32MiB 是 ~1.48s。我敢打赌那里有一个缓存限制。
  • @MooingDuck 确实与缓存级别的大小有关。但请注意,在这种特殊情况下,在超出缓存后 - “缓存中”的概率开始逐渐降低,而不是像里程碑那样尖锐。 getconf -a|grep -i cache 用于 Coliru reports 以下缓存大小:L1=64KiB、L2=512KiB、L3=6MiB。虽然可以仅通过基准测试来调查缓存边界,但其他数据访问模式更适合此目的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多