Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C:访问查找表更快?_C_Linux_Optimization_Compiler Optimization - Fatal编程技术网

C:访问查找表更快?

C:访问查找表更快?,c,linux,optimization,compiler-optimization,C,Linux,Optimization,Compiler Optimization,我有一段代码,一次跟踪4个正弦 我的系统每帧大约进行12000次sin()函数调用,以每秒30帧的速度运行 我试图通过生成查找表来优化它。我最终得到了16个不同的查找表。我声明并将它们加载到程序顶部的一个单独的头文件中。每个表的声明如下: static const float d4_lookup[800] {...}; 现在,用这种新方法我真的失去了fps?!我现在以每秒20帧的速度跑步,而不是30帧。每个帧现在只需要执行8个sin/cos调用和19200个查找调用,而sin()调用只有120

我有一段代码,一次跟踪4个正弦

我的系统每帧大约进行12000次sin()函数调用,以每秒30帧的速度运行

我试图通过生成查找表来优化它。我最终得到了16个不同的查找表。我声明并将它们加载到程序顶部的一个单独的头文件中。每个表的声明如下:

static const float d4_lookup[800] {...};
现在,用这种新方法我真的失去了fps?!我现在以每秒20帧的速度跑步,而不是30帧。每个帧现在只需要执行8个sin/cos调用和19200个查找调用,而sin()调用只有12000个。 我使用打开-O3标志的gcc进行编译。目前,查找表包含在顶部,是程序全局范围的一部分

我想我没有将它们加载到正确的内存或类似的内容中。如何加快查找时间

**编辑1**

根据要求,以下是使用查找调用的函数,每帧调用一次:

void
update_sines(void)
{
    static float c1_sin, c1_cos;
    static float c2_sin, c2_cos;
    static float c3_sin, c3_cos;
    static float c4_sin, c4_cos;

    clock_gettime(CLOCK_MONOTONIC, &spec);
    s = spec.tv_sec;
    ms = spec.tv_nsec * 0.0000001;
    etime = concatenate((long)s, ms);

    c1_sin = sinf(etime * 0.00525);
    c1_cos = cosf(etime * 0.00525);
    c2_sin = sinf(etime * 0.007326);
    c2_cos = cosf(etime * 0.007326);
    c3_sin = sinf(etime * 0.0046);
    c3_cos = cosf(etime * 0.0046);
    c4_sin = sinf(etime * 0.007992);
    c4_cos = cosf(etime * 0.007992);

    int k;
    for (k = 0; k < 800; ++k)
    {       
        sine1[k] = a1_lookup[k] * ((bx1_sin_lookup[k] * c1_cos) + (c1_sin * bx1_cos_lookup[k])) + d1_lookup[k];
        sine2[k] = a2_lookup[k] * ((bx2_sin_lookup[k] * c2_cos) + (c2_sin * bx2_cos_lookup[k])) + d2_lookup[k] + 50;
        sine3[k] = a3_lookup[k] * ((bx3_sin_lookup[k] * c3_cos) + (c3_sin * bx3_cos_lookup[k])) + d3_lookup[k];
        sine4[k] = a4_lookup[k] * ((bx4_sin_lookup[k] * c4_cos) + (c4_sin * bx4_cos_lookup[k])) + d4_lookup[k] + 50;
    }

}
void
更新(无效)
{
静态浮点数c1_sin,c1_cos;
静态浮点数c2_sin,c2_cos;
静态浮动c3_sin,c3_cos;
静态浮动c4_sin,c4_cos;
时钟获取时间(时钟单调,规格);
s=规格电视秒;
ms=spec.tv\u nsec*0.0000001;
时间=串联((长)s,ms);
c1_sin=sinf(时间*0.00525);
c1_cos=cosf(时间*0.00525);
c2_sin=sinf(时间*0.007326);
c2_cos=cosf(时间*0.007326);
c3_sin=sinf(时间*0.0046);
c3_cos=cosf(etime*0.0046);
c4_sin=sinf(时间*0.007992);
c4_cos=cosf(时间=0.007992);
int k;
对于(k=0;k<800;++k)
{       
sine1[k]=a1_查找[k]*(bx1_sinu查找[k]*c1_cos)+(c1_sin*bx1_cos查找[k])+d1_查找[k];
sine2[k]=a2_查找[k]*((bx2_sinu查找[k]*c2_cos)+(c2_sin*bx2_cos查找[k]))+d2_查找[k]+50;
sine3[k]=a3_查找[k]*(bx3_sinu查找[k]*c3_cos)+(c3_sin*bx3_cos查找[k])+d3_查找[k];
sine4[k]=a4_查找[k]*((bx4_sinu查找[k]*c4_cos)+(c4_sin*bx4_cos查找[k]))+d4_查找[k]+50;
}
}
**更新**


对于任何阅读这篇文章的人,我放弃了这个问题。我尝试使用OpenCL内核、结构、SIMD指令以及这里显示的所有解决方案。最后,计算每帧sinf()12800的原始代码比查找表工作得更快,因为查找表不适合缓存。然而,它仍然只有30帧每秒。它只是有太多的事情发生,以跟上我的60帧的期望。我决定换个方向。感谢所有为这篇文章做出贡献的人。这些解决方案中的大多数都可能会获得一些不错的速度提升,但没有比我在这里需要的200%的速度更高的了,以便让查找表按我想要的方式工作。

尝试像这样展开循环:

for (k = 0; k < 800; ++k)
{       
    sine1[k] = a1_lookup[k];
    sine2[k] = a2_lookup[k];
    sine3[k] = a3_lookup[k];
    sine4[k] = a4_lookup[k];
}
for (k = 0; k < 800; ++k)
{       
    sine1[k] *= ((bx1_sin_lookup[k] * c1_cos) + (c1_sin * bx1_cos_lookup[k]));
    sine2[k] *= ((bx2_sin_lookup[k] * c2_cos) + (c2_sin * bx2_cos_lookup[k]));
    sine3[k] *= ((bx3_sin_lookup[k] * c3_cos) + (c3_sin * bx3_cos_lookup[k]));
    sine4[k] *= ((bx4_sin_lookup[k] * c4_cos) + (c4_sin * bx4_cos_lookup[k]));
}
for (k = 0; k < 800; ++k)
{       
    sine1[k] += d1_lookup[k];
    sine2[k] += d2_lookup[k] + 50;
    sine3[k] += d3_lookup[k];
    sine4[k] += d4_lookup[k] + 50;
}
for(k=0;k<800;++k)
{       
sine1[k]=a1_查找[k];
sine2[k]=a2_查找[k];
sine3[k]=a3_查找[k];
sine4[k]=a4_查找[k];
}
对于(k=0;k<800;++k)
{       
sine1[k]*=((bx1_sin_查找[k]*c1_cos)+(c1_sin*bx1_cos_查找[k]);
sine2[k]*=((bx2_sin_查找[k]*c2_cos)+(c2_sin*bx2_cos_查找[k]);
sine3[k]*=((bx3_sin_查找[k]*c3_cos)+(c3_sin*bx3_cos查找[k]);
sine4[k]*=((bx4_sin_查找[k]*c4_cos)+(c4_sin*bx4_cos查找[k]);
}
对于(k=0;k<800;++k)
{       
sine1[k]+=d1_查找[k];
sine2[k]+=d2_查找[k]+50;
sine3[k]+=d3_查找[k];
sine4[k]+=d4_查找[k]+50;
}

通过在每个循环中访问较少的查找表,您应该能够留在缓存中。中间循环也可以拆分,但您需要为其中一个子表达式创建一个中间表。

有时很难知道是什么让您减速,但可能会破坏缓存命中,您可以尝试查找结构

typedef struct 
{
  float bx1_sin;
  float bx2_sin;
  float bx3_sin;
  float bx4_sin;
  float bx1_cos;
 etc etc
 including  sine1,2,3,4 as well

} lookup_table
然后

现在,第k次查找的所有内容都将位于同一小块内存中

此外,如果使用以k为参数的宏来执行循环的内容,可以说是
SINE\u CALC(k)
,或者使用内联函数

你能行

for (k = 0; k < 800; ++k)
{
  SINE_CALC(k); k++;
  SINE_CALC(k); k++;
  SINE_CALC(k); k++;
  SINE_CALC(k); k++;
  SINE_CALC(k); k++;
}
for(k=0;k<800;++k)
{
正弦计算(k);k++;
正弦计算(k);k++;
正弦计算(k);k++;
正弦计算(k);k++;
正弦计算(k);k++;
}

如果执行宏,请确保
k++
在宏调用之外,如图所示

使用简单的
sin
查找表将在我的linux机器(vm、gcc、64位)上产生>20%的速度提升。有趣的是,查找表的大小(在合理的 使用fastsin简单的实现,我获得了超过45%的改进

代码:


英特尔处理器可以预测最多4个阵列的串行访问(并执行预取),以实现正向和反向遍历。至少在Core 2 Duo days中是这样。将您的帐户拆分为:

for (k = 0; k < 800; ++k)
    sine1[k] = a1_lookup[k] * ((bx1_sin_lookup[k] * c1_cos) + (c1_sin * bx1_cos_lookup[k])) + d1_lookup[k];
for (k = 0; k < 800; ++k)
    sine2[k] = a2_lookup[k] * ((bx2_sin_lookup[k] * c2_cos) + (c2_sin * bx2_cos_lookup[k])) + d2_lookup[k] + 50;
for (k = 0; k < 800; ++k)
    sine3[k] = a3_lookup[k] * ((bx3_sin_lookup[k] * c3_cos) + (c3_sin * bx3_cos_lookup[k])) + d3_lookup[k];
for (k = 0; k < 800; ++k)
    sine4[k] = a4_lookup[k] * ((bx4_sin_lookup[k] * c4_cos) + (c4_sin * bx4_cos_lookup[k])) + d4_lookup[k] + 50;
for(k=0;k<800;++k)
sine1[k]=a1_查找[k]*(bx1_sinu查找[k]*c1_cos)+(c1_sin*bx1_cos查找[k])+d1_查找[k];
对于(k=0;k<800;++k)
sine2[k]=a2_查找[k]*((bx2_sinu查找[k]*c2_cos)+(c2_sin*bx2_cos查找[k]))+d2_查找[k]+50;
对于(k=0;k<800;++k)
sine3[k]=a3_查找[k]*(bx3_sinu查找[k]*c3_cos)+(c3_sin*bx3_cos查找[k])+d3_查找[k];
对于(k=0;k<800;++k)
sine4[k]=a4_查找[k]*((bx4_sinu查找[k]*c4_cos)+(c4_sin*bx4_cos查找[k]))+d4_查找[k]+50;

我猜你的缓存负载比其他答案中的基准要多,所以这很重要。我建议您不要展开循环,编译器会做得很好。

为什么您有19200个查找调用而不是12000个sin()调用?如何访问查找表以及如何计算索引?如果您希望我们找出错误的原因,请显示您的代码。@MartinPerry只需像a1_lookup[I]那样调用该表即可我需要值的地方。为什么查找范围在0..799?似乎是威尔
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>

#define LOOKUP_SIZE 628


uint64_t currentTimestampUs( void )
{
  struct timeval tv;
  time_t localTimeRet;
  uint64_t timestamp = 0;
  //time_t tzDiff = 0;
  struct tm when;
  int64_t localeOffset = 0;

  {
    localTimeRet = time(NULL);
    localtime_r ( &localTimeRet, &when );
    localeOffset = when.tm_gmtoff * 1000000ll;
  }

  gettimeofday ( &tv, NULL );
  timestamp = ((uint64_t)((tv.tv_sec) * 1000000ll) ) + ( (uint64_t)(tv.tv_usec) );
  timestamp+=localeOffset;

  return timestamp;
}


const double PI   = 3.141592653589793238462;
const double PI2  = 3.141592653589793238462 * 2;

static float sinarr[LOOKUP_SIZE];

void initSinArr() {
  int a =0;
  for (a=0; a<LOOKUP_SIZE; a++) {
    double arg = (1.0*a/LOOKUP_SIZE)*((double)PI * 0.5);
    float sinval_f = sin(arg); // double computation earlier to avoid losing precision on value
    sinarr[a] = sinval_f;
  }
}

float sinlookup(float val) {
  float normval = val;
  while (normval < 0) {
    normval += PI2;
  }
  while (normval > PI2) {
    normval -= PI2;
  }
  int index = LOOKUP_SIZE*(2*normval/PI);

  if (index > 3*LOOKUP_SIZE) {
    index = -index + 4*LOOKUP_SIZE;//LOOKUP_SIZE - (index-3*LOOKUP_SIZE);
    return -sinarr[index];
  } else if (index > 2*LOOKUP_SIZE) {
    index = index - 2*LOOKUP_SIZE;
    return -sinarr[index];
  } else if (index > LOOKUP_SIZE) {
    index = 2*LOOKUP_SIZE - index;
    return sinarr[index];
  } else {
    return sinarr[index];
  }
}


float sin_fast(float x) {
  while (x < -PI)
      x += PI2;

  while (x >  PI)
      x -= PI2;

  //compute sine
  if (x < 0)
      return 1.27323954 * x + .405284735 * x * x;
  else
      return 1.27323954 * x - 0.405284735 * x * x;

}

int main(void) {
  initSinArr();
  int a = 0;
  float val = 0;
  const int num_tries = 100000;

  uint64_t startLookup = currentTimestampUs();

  for (a=0; a<num_tries; a++) {
    for (val=0; val<PI2; val+=0.01) {
      float compval = sinlookup(val);
      (void)compval;
    }
  }

  uint64_t startSin = currentTimestampUs();

  for (a=0; a<num_tries; a++) {
    for (val=0; val<PI2; val+=0.01) {
      float compval = sin(val);
      (void)compval;
    }
  }

  uint64_t startFastSin = currentTimestampUs();

  for (a=0; a<num_tries; a++) {
    for (val=0; val<PI2; val+=0.01) {
      float compval = sin_fast(val);
      (void)compval;
    }
  }
  uint64_t end = currentTimestampUs();

  int64_t lookupMs = (startSin - startLookup)/1000;
  int64_t sinMs = (startFastSin - startSin)/1000;
  int64_t fastSinMs = (end - startFastSin)/1000;
  printf(" lookup: %lld ms\n", lookupMs );
  printf("    sin: %lld ms\n", sinMs );
  printf("   diff: %lld ms\n", sinMs-lookupMs);
  printf("  diff%: %lld %\n", 100*(sinMs-lookupMs)/sinMs);

  printf("fastsin: %lld ms\n", fastSinMs );
  printf("    sin: %lld ms\n", sinMs );
  printf("   diff: %lld ms\n", sinMs-fastSinMs);
  printf("  diff%: %lld %\n", 100*(sinMs-fastSinMs)/sinMs);
}
 lookup: 2276 ms
    sin: 3004 ms
   diff: 728 ms
  diff%: 24 %
fastsin: 1500 ms
    sin: 3004 ms
   diff: 1504 ms
  diff%: 50 %
for (k = 0; k < 800; ++k)
    sine1[k] = a1_lookup[k] * ((bx1_sin_lookup[k] * c1_cos) + (c1_sin * bx1_cos_lookup[k])) + d1_lookup[k];
for (k = 0; k < 800; ++k)
    sine2[k] = a2_lookup[k] * ((bx2_sin_lookup[k] * c2_cos) + (c2_sin * bx2_cos_lookup[k])) + d2_lookup[k] + 50;
for (k = 0; k < 800; ++k)
    sine3[k] = a3_lookup[k] * ((bx3_sin_lookup[k] * c3_cos) + (c3_sin * bx3_cos_lookup[k])) + d3_lookup[k];
for (k = 0; k < 800; ++k)
    sine4[k] = a4_lookup[k] * ((bx4_sin_lookup[k] * c4_cos) + (c4_sin * bx4_cos_lookup[k])) + d4_lookup[k] + 50;