优化C循环

优化C循环,c,optimization,loops,C,Optimization,Loops,我是多年来在Matlab从事数值编程的新手。我开发了一个求解大型微分方程组的程序,但我敢肯定我做了一些愚蠢的事情,因为在分析代码之后,我惊讶地看到三个循环占用了约90%的计算时间,尽管它们执行的是程序中最琐碎的步骤 基于这些昂贵的循环,我的问题分为三个部分: 将数组初始化为零。当J被声明为双数组时,数组的值是否初始化为零?如果没有,是否有快速的方法将所有元素设置为零 void spam(){ double J[151][151]; /* Other relevant v

我是多年来在Matlab从事数值编程的新手。我开发了一个求解大型微分方程组的程序,但我敢肯定我做了一些愚蠢的事情,因为在分析代码之后,我惊讶地看到三个循环占用了约90%的计算时间,尽管它们执行的是程序中最琐碎的步骤

基于这些昂贵的循环,我的问题分为三个部分:

  • 将数组初始化为零。当J被声明为双数组时,数组的值是否初始化为零?如果没有,是否有快速的方法将所有元素设置为零

    void spam(){
        double J[151][151];    
        /* Other relevant variables declared */
        calcJac(data,J,y);
        /* Use J */
    }
    
    static void calcJac(UserData data, double J[151][151],N_Vector y)
    {
        /* The first expensive loop */
        int iter, jter;
        for (iter=0; iter<151; iter++) {
            for (jter = 0; jter<151; jter++) {
                J[iter][jter] = 0;
            }
        }
       /* More code to populate J from data and y that runs very quickly */
    }
    
    void spam(){
    双J[151][151];
    /*声明的其他相关变量*/
    calcJac(数据,J,y);
    /*使用J*/
    }
    静态void calcJac(UserData数据,双J[151][151],N_向量y)
    {
    /*第一个昂贵的循环*/
    国际热核实验堆;
    对于(iter=0;iter1)不,它们不是。您可以按如下方式设置阵列:

    memset( J, 0, sizeof( double ) * 151 * 151 );
    
    也可以使用数组初始化器:

    double J[151][151] = { 0.0 };
    
    2) 你用一个相当复杂的计算来计算p和J的位置

    您可能会获得更好的性能。通过作为指针逐步执行:

    for (iter = 1; iter<151; iter++) 
    {
        double* pP = (P - 1) + (151 * iter);
        double* pJ = data->J + (151 * iter);
    
        for(jter = 1; jter<151; jter++, pP++, pJ++ )
        {
             *pP = - gamma * *pJ;
        }
    }
    
    for(iter=1;iterJ+(151*iter);
    
    对于(jter=1;jter首先,我建议您将问题分为三个独立的问题。很难回答所有三个问题;例如,我对数值分析没有太多研究,所以我只回答第一个问题

    首先,堆栈上的变量不是为您初始化的。但是有更快的方法来初始化它们。在您的情况下,我建议使用memset:

    static void calcJac(UserData data, double J[151][151],N_Vector y)
    {
       memset((void*)J, 0, sizeof(double) * 151 * 151);
       /* More code to populate J from data and y that runs very quickly */
    }
    

    memset
    是一个快速库例程,用于使用特定的字节模式填充内存区域。将
    double
    的所有字节设置为零会将
    double
    设置为零,因此利用库的快速例程(可能会用汇编语言编写,以利用SSE等功能)。

    其他人已经回答了您的一些问题。关于矩阵乘法,除非您对缓存体系结构等有很多了解,否则很难为此编写快速算法(缓慢将由访问数组元素的顺序导致数千次缓存未命中造成)

    如果你想了解fast库中使用的技术,你可以在谷歌上搜索“矩阵乘法”、“缓存”、“阻塞”等术语。但我的建议是,如果性能是关键,就使用预先存在的数学库

    将数组初始化为零。 当J被声明为双精度时 数组是数组的值 初始化为零?如果不是,是否存在 将所有元素设置为 零

    这取决于数组的分配位置。如果它在文件范围内声明,或声明为静态,则C标准保证所有元素都设置为零。如果在初始化时将第一个元素设置为值,也可以保证为零,即:

    double J[151][151] = {0}; /* set first element to zero */
    
    通过将第一个元素设置为某个值,C标准保证数组中的所有其他元素都设置为零,就好像数组是静态分配的一样

    实际上,对于这种特定的情况,我非常怀疑在堆栈上分配151*151*sizeof(double)字节是否明智,无论您使用的是哪种系统。您可能需要动态分配它,然后上述任何一项都不重要。然后必须使用memset()将所有字节设置为零

    在 下面是一个相对较慢的循环 访问包含的矩阵 在“数据”结构中,速度较慢 组件还是其他东西 关于环路

    for (iter = 1; iter<151; iter++) {
        for(jter = 1; jter<151; jter++){
            P[iter-1][jter-1] = - gamma*(data->J[iter][jter]);
        }
    }
    
    您应该确保从中调用的函数是内联的。否则,您就没有其他方法可以优化循环:优化的是高度依赖于系统的(即物理缓存内存的构建方式)。最好将这种优化留给编译器

    当然,您可以使用手动优化的方法来混淆代码,例如倒计时而不是倒计时,或者使用++i而不是i++等等。但是编译器确实应该能够为您处理这些事情


    关于矩阵加法,我不知道数学上最有效的方法,但我怀疑它与代码的效率无关。这里的大时间小偷是双类型的。除非你真的需要高精度,否则我会考虑使用浮点或int来加速算法。

    @ GOZ,我不认为你的第二种方法是INI。将数组元素化为零将起到双j[151][151]={0.0}的作用,这只会使j[0][0]最小化到零。@Algorithmist:你试过了吗?我的经验是使用数组初始化器用给定的值初始化整个数组…@eckes:他可能是对的。这可能只是编译器特有的功能。但它在GCC和Visual Studio上工作……我假设,也许是错误的,这是C99的一个功能…@Goz:我怀疑指针技巧mak没有任何区别,因为现代编译器会自动完成。@Goz,C99§6.7.8/19:“初始化应按初始值设定项列表顺序进行,为特定子对象提供的每个初始值设定项将覆盖相同子对象的任何先前列出的初始值设定项;所有未显式初始化的子对象应隐式初始化,与具有静态存储持续时间的对象相同。”(emph.mine)似乎并非所有平台都将值
    0.0
    实现为所有零位值,因此
    memset
    对于浮点值不是一个好主意。有一种可预见的初始化器语法可以独立于平台工作。不幸的是,初始化器语法只能在初始声明时使用。但是d虽然严格来说,它不是可移植的,但大多数主要平台都有
    0.0=0x0000
    double J[151][151] = {0}; /* set first element to zero */