C集成运行太慢

00jrzges  于 2023-05-22  发布在  其他
关注(0)|答案(2)|浏览(73)

我正在尝试编写从-inf到inf的Romberg积分的c代码。这里写的是我给予数字并运行函数的地方。当大于20的数字是15或有时也是15时,代码不会运行或花费太长时间。
我很确定这是因为功能,但我真的不知道哪个部分应该改变。
先谢谢你了

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#define eps pow(10,-8)
double f (double a, double x, double z){
    return exp(-pow(x,2)/pow(a,2))/sqrt(pow(x,2)+pow(z,2));
}
                       
long double romberg(double f(double, double, double), double a, double xa, double xe, double z,int N){

    double *Rpre=malloc(N*sizeof(double));
    double *Rcur=malloc(N*sizeof(double));
    //double R1[n],R2[n];
    //double Rpre=&R1[0],*Rcur=&R2[0]; //Rpre fuer previous row, Rcur fuer current row
    double h =xe-xa;
    Rpre[0]=0.5*h*(f(a,xa,z)+f(a,xe,z)); //erste Trapaze
    
    for(int i=1;i<N;i++){
        h*=0.5;
        double sum=0;
        
        for(int j=1;j<=pow(2,i-1);j++){//bis 2^(n-1)
            sum+=f(a,xa+(2*j-1)*h,z);
        }
        Rcur[0]=h*sum+0.5*Rpre[0];//R[i][0]
        
        for(int j=1;j<=i;j++){
            double n_k=pow(4,j);
            Rcur[j]=(n_k*Rcur[j-1]-Rpre[j-1]) /(n_k-1); //R[i][j]
        }
        
       /* if(i>1&&fabs(Rpre[i-1]-Rcur[i])<eps){ //Wenn die rel. Genauigkeit erfuellt ist.
            return Rcur[i];
        }*/
       double *temp=Rpre; //wenn es bis zum Ende der Reihe laeuft, muss Current Wert Previous Wert sein
        Rpre=Rcur;
        Rcur=temp;
        
    }printf("%25.16e\n",Rpre[N-1]);
    return Rpre[N-1];
            

        
}
            
int main(int argc, char **argv) {
    double a[4]={0.02,1,2,4};
    double N=LONG_MAX; //maximale double wert
    double alpha=1/sqrt(M_PI);
    
    //long double ** R= malloc( N * sizeof(long double ) );
//printf("%lf",N);
    
 /* Parameter für den z-Bereich 
   */
  double const zmin = 0.;
  double const zmax = 5.;
  int const znum = 100;

  /* Schleife über z-Werte, für die integriert werden soll
   */
for (int i=0;i<4;i++){
    char filename[32];
    sprintf(filename, "h21_a_%.2lf.txt",a[i]);
    FILE *fp = fopen(filename, "w");
    
 for ( int iz = 0; iz <= znum; iz++ )
  {

    double z = zmin + ( zmax - zmin ) / (double)znum * iz ;
     double r=romberg(f,a[i],-N,N,z,20); //HERE
    fprintf(fp,"%lf\t%25.16e\n",z,r*alpha/a[i]);
     

  }fclose(fp);
}
   // free(R);
    return 0;
}

我将变量改为 var而不是var,并且只使用两个一维数组而不是R[n][n]二维数组。
++将Rpre和Rcur改为R,inf改为2^27,for-循环的int x = 1 << (i - 1); for (int j = 1; j <= x; j++) { pow(x,2)改为x
x

szqfcxe2

szqfcxe21#

我很确定这是因为功能,但我真的不知道哪个部分应该改变。
不,它是您正在使用的算法的特征。相对于romberg()的最后一个参数的值,您的计算扩展得非常快(O(2n)),这就是您所询问的。对于任何给定的函数,在程序运行的时间超过您愿意等待的时间之前,n 不必非常大,这并不奇怪。
你可以通过代码和编译的改进来稍微增加这个限制,但是即使使它的速度提高一倍,你也只能在任何固定时间内计算的 n 中增加1。
您还应该考虑一些计算方面的考虑,尤其是浮点表示。FP表示法并不精确,而且您选择的积分界限几乎肯定是两者

  • 非常远的范围内,您的函数的数学结果无法与系统的浮点表示中的零区分开来,并且
  • 非常远的范围,在这个范围内,函数计算中的中间结果会严重损失精度。

同时,使用如此大的边界意味着你需要去相当大的n来获得积分的良好估计,因为它在整个范围中的比例很小,对该估计有意义。
可能的算法改进包括:

  • 不要使用固定的 n,而是使用自适应方法,当其相对变化低于某个阈值(可能限制为最大 n)时,停止细化估计。
  • 缩小积分边界以实现更快的收敛(即,在较小的 n 处)。您可以考虑指数搜索函数值停止严格减少的点(超过这些点的任何内容都只是噪音)。或者对于你正在使用的特定形式的函数,你可以考虑使用+/- 227,因为如果你的double有53位尾数(大多数实现都是这样),这会让你刚好超过你开始失去精度的边缘。还有其他选择。
ccgok5k5

ccgok5k52#

一些调整(产生1.7倍加速)...
1.执行(x * x)pow(x,2)
1.对整数执行pow(2,j)是很慢的--它可以用移位来代替。
1.它应该只做 * 一次 *(并且 * 不是 * 在每次循环迭代中)。
1.在每次循环迭代中执行n_k = pow(4,j)很慢
1.可以替换为n_k *= 4.0
这是正确的代码。它标注了bug和修复。我还在printf调用中添加了基准计时:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <time.h>

#ifndef ORIG
#define ORIG    0
#endif
#define FIX     (! ORIG)

#if 0
#define eps pow(10,-8)
#endif
double eps;

#define xsqr(_x) \
    ((_x) * (_x))

double tsczero;

double
tscgetf(void)
{
    struct timespec ts;
    double sec;

    clock_gettime(CLOCK_MONOTONIC,&ts);

    sec = ts.tv_nsec;
    sec /= 1e9;
    sec += ts.tv_sec;

    sec -= tsczero;

    return sec;
}

double
f(double a, double x, double z)
{
// NOTE/BUG: doing (x * x) is faster than pow(x,2)
#if ORIG
    return exp(-pow(x, 2) / pow(a, 2)) / sqrt(pow(x, 2) + pow(z, 2));
#else
    double x2 = xsqr(x);
    return exp(-x2 / xsqr(a)) / sqrt(x2 + xsqr(z));
#endif
}

long double
romberg(double f(double, double, double), double a, double xa, double xe,
    double z, int N)
{

    double *Rpre = malloc(N * sizeof(double));
    double *Rcur = malloc(N * sizeof(double));

    // double R1[n],R2[n];
    // Rpre fuer previous row, Rcur fuer current row
    // double Rpre=&R1[0],*Rcur=&R2[0];
    double h = xe - xa;

    // erste Trapaze
    Rpre[0] = 0.5 * h * (f(a, xa, z) + f(a, xe, z));

    for (int i = 1; i < N; i++) {
        h *= 0.5;
        double sum = 0;

        // bis 2^(n-1)
// NOTE/BUG: doing pow(i,j) for integers is slow
// NOTE/BUG: calling this on each loop iteration is slow
#if ORIG
        for (int j = 1; j <= pow(2, i - 1); j++) {
            sum += f(a, xa + (2 * j - 1) * h, z);
        }
#else
// NOTE/FIX: replace pow with a shift and do only once
        for (int j = 1, j2 = 1 << (i - 1); j <= j2; j++) {
            sum += f(a, xa + (2 * j - 1) * h, z);
        }
#endif

        // R[i][0]
        Rcur[0] = h * sum + 0.5 * Rpre[0];

// NOTE/BUG: calling pow on each iteration is slow
#if ORIG
        for (int j = 1; j <= i; j++) {
            double n_k = pow(4, j);

            // R[i][j]
            Rcur[j] = (n_k * Rcur[j - 1] - Rpre[j - 1]) / (n_k - 1);
        }
#else
// NOTE/FIX: replace pow with incremental multiply
        double n_k = 1.0;
        for (int j = 1; j <= i; j++) {
            n_k *= 4.0;

            // R[i][j]
            Rcur[j] = (n_k * Rcur[j - 1] - Rpre[j - 1]) / (n_k - 1);
        }
#endif

#if EPS
        // Wenn die rel. Genauigkeit erfuellt ist.
        if (i > 1 && fabs(Rpre[i - 1] - Rcur[i]) < eps) {
            return Rcur[i];
        }
#endif
        // wenn es bis zum Ende der Reihe laeuft, muss Current Wert Previous
        // Wert sein
        double *temp = Rpre;

        Rpre = Rcur;
        Rcur = temp;
    }

// NOTE/BUG: don't include printf if doing performance measurements
#if 0
    printf("%25.16e\n", Rpre[N - 1]);
#endif

    return Rpre[N - 1];
}

int
main(int argc, char **argv)
{
    double a[4] = { 0.02, 1, 2, 4 };
    // maximale double wert
    double N = LONG_MAX;
    double alpha = 1 / sqrt(M_PI);

    tsczero = tscgetf();

#if 0
    long double **R = malloc(N * sizeof(long double));

    printf("%lf", N);
#endif

    eps = pow(10,-8);

    // Parameter für den z-Bereich
    double const zmin = 0.;
    double const zmax = 5.;
#if DEMO
    int const znum = DEMO;
#else
    int const znum = 100;
#endif

    // Schleife über z-Werte, für die integriert werden soll
    for (int i = 0; i < 4; i++) {
        char filename[32];

        sprintf(filename, "h21_a_%.2lf.txt", a[i]);
        FILE *fp = fopen(filename, "w");

        for (int iz = 0; iz <= znum; iz++) {
            double tscbeg = tscgetf();

            double z = zmin + (zmax - zmin) / (double) znum * iz;

            // HERE
            double r = romberg(f, a[i], -N, N, z, 20);

#if 1
            double tscend = tscgetf();
            printf("%.9f %25.16e\n", tscend - tscbeg, r);
            tscbeg = tscend;
#endif

#if 1
            fprintf(fp, "%lf\t%25.16e\n", z, r * alpha / a[i]);
#endif
        }

        fclose(fp);
    }

#if 0
    free(R);
#endif

    return 0;
}

在上面的代码中,我使用了cpp条件来表示old与新代码:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

还有...

#if ORIG
// old code
#else
// new code
#endif

#if FIX
// new code
#endif

注意:这可以通过unifdef -k -DORIG=0运行文件来清理。
循环迭代的原始版本和固定版本的运行时间:

Total time  Time per call
ORIG 4.069441405 0.092487305
FIX  2.395335764 0.054439449

加速比为1.699x
下面是原始程序 * 和 * 修复程序(用-DDEMO=10编译)的输出。

ORIG                                    FIX
0.098137318                      -nan   0.057743992                      -nan
0.097679134    4.2865100183969344e+13   0.055832875    4.2865100183969344e+13
0.097967627    2.1432550091984672e+13   0.055699762    2.1432550091984672e+13
0.096517395    1.4288366727989789e+13   0.055477580    1.4288366727989789e+13
0.094043442    1.0716275045992336e+13   0.056299677    1.0716275045992336e+13
0.093548951    8.5730200367938691e+12   0.058548293    8.5730200367938691e+12
0.095909685    7.1441833639948945e+12   0.055331890    7.1441833639948945e+12
0.094319706    6.1235857405670557e+12   0.055480244    6.1235857405670557e+12
0.094109384    5.3581375229961680e+12   0.055346522    5.3581375229961680e+12
0.093769347    4.7627889093299268e+12   0.056081215    4.7627889093299268e+12
0.093589583    4.2865100183969346e+12   0.055978985    4.2865100183969346e+12
0.091371239                      -nan   0.053419079                      -nan
0.092579969    4.2865100183969344e+13   0.053669294    4.2865100183969344e+13
0.091236377    2.1432550091984672e+13   0.054249677    2.1432550091984672e+13
0.090961806    1.4288366727989789e+13   0.054563181    1.4288366727989789e+13
0.090794377    1.0716275045992336e+13   0.053247944    1.0716275045992336e+13
0.090866636    8.5730200367938691e+12   0.053357693    8.5730200367938691e+12
0.092706834    7.1441833639948945e+12   0.053283874    7.1441833639948945e+12
0.090963371    6.1235857405670557e+12   0.053576597    6.1235857405670557e+12
0.091215859    5.3581375229961680e+12   0.053465706    5.3581375229961680e+12
0.091968977    4.7627889093299268e+12   0.054223244    4.7627889093299268e+12
0.091215764    4.2865100183969346e+12   0.054221564    4.2865100183969346e+12
0.091439863                      -nan   0.053886837                      -nan
0.091878234    4.2865100183969344e+13   0.056147531    4.2865100183969344e+13
0.091113353    2.1432550091984672e+13   0.053652313    2.1432550091984672e+13
0.091204614    1.4288366727989789e+13   0.053591324    1.4288366727989789e+13
0.091964998    1.0716275045992336e+13   0.053493202    1.0716275045992336e+13
0.091647607    8.5730200367938691e+12   0.053533189    8.5730200367938691e+12
0.092162252    7.1441833639948945e+12   0.053956118    7.1441833639948945e+12
0.090839403    6.1235857405670557e+12   0.054470336    6.1235857405670557e+12
0.090834735    5.3581375229961680e+12   0.053291850    5.3581375229961680e+12
0.092375710    4.7627889093299268e+12   0.053635458    4.7627889093299268e+12
0.091070728    4.2865100183969346e+12   0.056126570    4.2865100183969346e+12
0.092109321                      -nan   0.053610519                      -nan
0.091759439    4.2865100183969344e+13   0.053366779    4.2865100183969344e+13
0.092044124    2.1432550091984672e+13   0.053542467    2.1432550091984672e+13
0.090677761    1.4288366727989789e+13   0.053449231    1.4288366727989789e+13
0.091415760    1.0716275045992336e+13   0.053615699    1.0716275045992336e+13
0.090851821    8.5730200367938691e+12   0.053385493    8.5730200367938691e+12
0.093354997    7.1441833639948945e+12   0.053705318    7.1441833639948945e+12
0.091455909    6.1235857405670557e+12   0.053308070    6.1235857405670557e+12
0.091560392    5.3581375229961680e+12   0.055094212    5.3581375229961680e+12
0.091412582    4.7627889093299268e+12   0.054127111    4.7627889093299268e+12
0.090795021    4.2865100183969346e+12   0.053247249    4.2865100183969346e+12

相关问题