【问题标题】:performance tuning a thrust application调整推力应用程序的性能
【发布时间】:2012-10-13 03:40:13
【问题描述】:

我正在我的带有 9600M GT gpu 的 macbook pro 上运行一个小型 C++/thrust 程序(如下),我有兴趣了解函数 h 的时间花在哪里,因为目标是尽可能快地运行此代码更大的 NEPS 值是可能的。

为此,我在函数中添加了 clock() 调用。

打印的时间表明几乎所有时间都花在了thrust::reduce 上。 事实上,thrust::reduce 报告的时间是thrust::transform 的数百倍,它对每个元素调用三个余弦调用。为什么?

当然,我对测量的时间持怀疑态度。 我插入了第二次调用thrust::reduce 只是为了看看报告的时间是否相似:它不是。第二次调用报告的时间具有更高的差异并且更小。 更多困惑:为什么?

我还尝试使用thrust::transform_reduce(已注释掉)来代替期望运行得更快的两个内核调用——相反,它慢了4%。为什么?

建议赞赏!

#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <iostream>

#include <stdio.h>
#include <stdint.h>


 float NEPS = 6.0;
 __device__ float EPS;
 __device__ float SQEPS;

 __device__ float CNV_win;
 __device__ float CNV_dt;
 int CNV_n;
 float EU_dt;

__host__ __device__ float f(float x,float t){
    return x*cos(t)+x*cos(t/SQEPS)+cos(t/EPS);
}

struct h_functor
{
  const float x, t;
  h_functor(float _x, float _t) : x(_x),t(_t) {}
  __host__ __device__
  float operator()(const float & t_f) const {
    return f(x,   t-CNV_win+CNV_dt*(t_f+1)   )*CNV_dt;
  } 
};


clock_t my_clock() __attribute__ ((noinline));
clock_t my_clock() {
  return clock();
}
float h(float x,float t){
    float sum;

    sum = CNV_dt*(f(x,t-CNV_win/2)+f(x,t+CNV_win/2))/2;
    clock_t start = my_clock(), diff1, diff2, diff3, diff4, diff5;
    thrust::device_vector<float> t_f(CNV_n-2);
    diff1 = my_clock() - start;
    /* initialize t_f to 0.. CNV_n-3 */
    start = my_clock();
    thrust::sequence(t_f.begin(), t_f.end());
    diff2 = my_clock() - start;

    start = my_clock();
    thrust::transform(t_f.begin(), t_f.end(), t_f.begin(), h_functor(x,t));
    diff3 = my_clock() - start;
    start = my_clock();
    sum += thrust::reduce(t_f.begin(), t_f.end());
    diff4 = my_clock() - start;
    start = my_clock();
    sum += thrust::reduce(t_f.begin(), t_f.end());
    diff5 = my_clock() - start;
#define usec(d) (d)
    fprintf(stderr, "Time taken %ld %ld %ld %ld %ld usecs\n", usec(diff1), usec(diff2), usec(diff3), usec(diff4), usec(diff5));
        /* a bit slower, surprisingly:
       sum += thrust::transform_reduce(t_f.begin(), t_f.end(), h_functor(x,t), 0, thrust::plus<float>());
       */

    return sum;
}
main(int argc, char ** argv) {
  if (argc >= 1) NEPS = strtod(argv[1], 0);
  fprintf(stderr, "NEPS = %g\n", NEPS);

  EPS= powf(10.0,-NEPS);
  SQEPS= powf(10.0,-NEPS/2.0);
  CNV_win= powf(EPS,1.0/4.0);
  CNV_dt = EPS;
  CNV_n = powf(EPS,-3.0/4.0);
  EU_dt = powf(EPS,3.0/4.0);

  cudaMemcpyToSymbol(CNV_win, &CNV_win, sizeof(float));
  cudaMemcpyToSymbol(CNV_dt, &CNV_dt, sizeof(float));
  cudaMemcpyToSymbol(SQEPS, &SQEPS, sizeof(float));
  cudaMemcpyToSymbol(EPS, &EPS, sizeof(float));

  float x=1.0;
  float t = 0.0;
  int n = floor(1.0/EU_dt);
  fprintf(stderr, "CNV_n = %d\n", CNV_n);
  while (n--) {
    float sum = h(x,t);
    x=x+EU_dt*sum;
    t=t+EU_dt;
  }
  printf("%f\n",x);
}

【问题讨论】:

  • 阅读This 答案。它可能对您的代码有用。
  • @abinhole:谢谢!在时钟调用之前添加 cudaDeviceSynchronize() 调用会产生更合理的结果。

标签: c++ performance thrust benchmarking


【解决方案1】:

如果您想优化算法以提高性能,则可以选择使用 arrayfire。我冒昧地为 arrayfire 重写了您的代码,您可以将其与推力版本进行比较并选择运行速度更快的代码:

float h(float x,float t){

 float sum = CNV_dt * (f(x, t - CNV_win/2) + f(x, t + CNV_win/2)) / 2;
 // initialize t_f with a sequence 0..CNV_n-3
 af::array t_f(af::seq(0, CNV_n-3));

 // transform vector on the GPU
 t_f =  t - CNV_win + CNV_dt*(t_f+1); 
 t_f = (x*cos(t_f) + x*cos(t_f/SQEPS) + cos(t_f/EPS)) * CNV_dt;

 sum += af::sum<float>(t_f); // sum up all elements of the vector
 return sum;
}

另外,请注意不需要将变量显式复制到 GPU(即不需要 cudaMemcpyToSymbol 调用)

【讨论】:

    【解决方案2】:

    在多核环境下最好不要使用clock()函数。给出错误的答案是明智的。

    最好使用挂钟时间clock_gettime。在 Windows 中,我们也有一些高分辨率的计时器。

    在使用 CUDA 时,最好使用 CUDA 本身提供的计时器。 cutil_timer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 2013-09-27
      • 2016-10-16
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      相关资源
      最近更新 更多