优化大量迭代的代码

时间:2012-07-20 16:21:09

标签: c++ optimization

我目前正在开展一个涉及大量迭代的项目(确切地说是2 ^ 32)。我主要使用mathematica进行大部分计算,但它无法处理那么多的进程。有人向我建议c ++能够处理它,所以昨晚我学习了c ++并编写了以下代码:

//old code  

代码运行正常,(我检查了较小的参数)但是我已经开始运行它为4294967295 = 2 ^ 32-1步骤,我认为这将花费数百小时。如果有人能告诉我是否有办法优化这些代码的位以使它运行得更快,我将非常感激?我没有这种语言的经验,所以我构建函数可能看起来很混乱。我认为我的Ca2step函数运行非常有效(我可能错了),我认为它在主要部分中的循环会减慢一切。我认为必须有更快的方法来实现我想要完成的任务,所以任何帮助都会很棒。 谢谢, 理查德。

=======更新========

非常感谢大家,我真的很感激。好吧,这对我来说都是新鲜事,所以我发现很难理解一些事情的含义。以下是我更新的代码。但是我觉得它仍然很慢。有些人建议“并行化”,但我不知道这是什么以及我将如何做到这一点?再次感谢Richard。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//parameters
int a[32] = {0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
             1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1};
int b[32] = {1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 
             1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};
// Create vector of vectors from arrays to be input into function.
vector<int> va (a, a + sizeof(a) / sizeof(int) );
vector<int> vb (b, b + sizeof(b) / sizeof(int) );

vector< vector<int> > ca2step (long int r, vector< vector<int> > vec)
{
    int rulearray[32] = { 0 };
    for (int pos = 31; pos >= 0; --pos){
        if (r % 2) 
            rulearray[pos] = 1;
        r /= 2;
    }
    int arraya[32] = {0};
    int arrayb[32] = {0};
    for (int i = 0; i < 32; i++) {
        arraya[i] = vec[0][i];
        arrayb[i] = vec[1][i];
    }

    vector< vector<int> > output;
    typedef int t_array[32];
    t_array vll, vl, vr, vrr, vx;

    rotate_copy(arrayb,arrayb+2,arrayb+32,vll);
    rotate_copy(arrayb,arrayb+1,arrayb+32,vl);    
    rotate_copy(arrayb,arrayb+31,arrayb+32,vr);    
    rotate_copy(arrayb,arrayb+30,arrayb+32,vrr);


    for (int i = 0; i < 32; i++) {
        vx[i] = (arraya[i] + rulearray[(31 - (vll[i] + (2 * vl[i]) 
                                           + (4 * arrayb[i]) + (8 * vr[i]) + (16 * vrr[i])))]) % 2;
    }

    output.push_back(vector<int>(arrayb, arrayb+32));
    output.push_back(vector<int>(vx, vx+32));

    return (output);

}

int caevolve ( long int r, vector< vector<int> > vector ){
    int count;
    for(int j=0; j<20; j++){ 
        //run function
        vector = ca2step(r, vector);
    }
    if (vector[0] == va || vector[1] == va) {
        count = 1;
        }
    else{
        count=0;
    }
    return (count);
}

int main ()
{
    vector< vector<int> > vinput;
    vinput.reserve(32);
    vinput.push_back(va);
    vinput.push_back(vb); 
    int counter = 0;

    for(unsigned long long int i=0;i<4294967295;i++){  //4294967295
        counter += caevolve(i, vinput);
        }

    cout<< "Counter : " << counter << endl;

    return 0;

}

10 个答案:

答案 0 :(得分:3)

除了C ++性能之外,您还应该考虑并行化代码并利用多核架构。在我看来,你的问题是一个经典的例子。

答案 1 :(得分:1)

杰克已经正确地确定了向量内的内存分配可能是一笔可观的费用。因此,将向量移到循环之外,只需clear(),而不是创建全新的向量。

这将为每次迭代每个向量保存至少一次分配/释放。

不要按值传递向量,而是使用const vector<vector<int>>&作为ca2step的参数类型。这将为内部循环的每次迭代保存一大堆矢量副本(以及内存分配和释放),这是一大堆。

ca2step内,使用堆栈数组(可能是std::array)而不是向量。这样可以节省更多的动态内存分配。 begin(arrayb)适用于数组和向量(而不是arrayb.begin())。

答案 2 :(得分:1)

仪器/配置文件并运行您的代码,例如十万或一百万次迭代。确定代码中花费大量执行时间的部分。尝试并改善这些部分的性能。重复。只有当您对自己不能进一步改善感到满意时,才应该尝试将其运行超过四次十亿次

答案 3 :(得分:1)

有太多的数组访问。您需要预取或更多本地来表示那些重新获取的数组元素。缓存友好。在这里阅读

http://www.research.scea.com/research/pdfs/GDC2003_Memory_Optimization_18Mar03.pdf

答案 4 :(得分:1)

将所有向量移到ca2step函数之外;使它们成为全局变量。在vector::reserve()开始push_back()之前,使用ca2step扩展其大小,您知道所有尺寸。由于vector::clear()现在可以处理它外部的数组,因此不需要返回任何内容,因此不需要两个向量的向量;直接使用这两个向量,当你完成后,只需unsigned long

此外,您可能需要将循环变量类型更改为unsigned long long或{{1}}。

答案 5 :(得分:1)

这应该由编译器在某种程度上完成。在您的情况下,您应该尝试并行化代码。

答案 6 :(得分:0)

您可以使用LinkedList而不是vector。 LinkedLists具有更快的插入(向量的push_back),因为它们永远不需要自己调整大小,这在很大程度上可能是一项代价高昂的操作。

答案 7 :(得分:0)

我认为你可以摆脱初始循环来填充规则阵列,替换为r:上的位测试来测试第n位,你可以使用

(r & (1 << nth)) ? 1 : 0 ...

然后使用规则阵列可以用

代替
arraya[i] + (r & (1 << (31 - (vll[i] + (2 * vl[i]) + (4 * arrayb[i]) + (8 * vr[i]) + (16 * vrr[i])) ?  1 : 0)

rotate_copy可以与普通旧数组一起使用:并且可以避免使用它进行大量动态内存分配,因为所有大小都是固定的。使用typedef强制执行此操作:

 typedef int t_array[32];
 t_array arraya, arrayb, vll, vl, vr, vrr, vx;

 rotate_copy(arrayb,arrayb+2,arrayb+32,vll);
 rotate_copy(arrayb,arrayb+1,arrayb+32,vl);    
 rotate_copy(arrayb,arrayb+31,arrayb+32,vr);    
 rotate_copy(arrayb,arrayb+30,arrayb+32,vrr);

然后只需要最终返回值需要堆栈分配数组的副本:

  output.push_back(vector<int>(arrayb,arrayb+32));
  output.push_back(vector<int>(vx,vx+32));

答案 8 :(得分:0)

感谢所有的帮助,我终于在合理的时间内(大约11个小时)完成了这项工作。我以为我会分享我的代码。我将需要在接下来的几周内多次运行,所以如果有任何其他技巧我可以用来进一步缩短时间,建议将不胜感激!

    #include <iostream>
using namespace std;

bool is_equal ( int a[], int b[]){
    for (int i=0; i<32; i++ ){
        if ( a[i] != b[i] )
            return false;
    }
    return true;
}

int ca2step (long long int rule, int arraya[32], int arrayb[32] ){
    int count =0;
    int x[32];
    int y[32];
    for(int i=0;i<32;i++){
        x[i] = arraya[i];
        y[i] = arrayb[i];
    }

    for (int j=0; j<19;j++){
            int arrayc[32];
            for (int i=0; i<32; i++){
            arrayc[i] = (x[i] + ((rule >> ( y[(i+2)%32] + (2 * y[(i+1)%32]) + 
                   (4 * y[i]) + (8 * y[(i+31)%32]) + (16 * y[(i+30)%32])) )& 1))%2;
            }

            for(int k=0;k<32;k++){ 
                x[k] = y[k];
                y[k] = arrayc[k];}
    }

    if(is_equal(y, arraya) || is_equal(y, arrayb)){
        count++;}  
    return(count);     
}

int main (){
    int a[32] = {0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 
                 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1};
    int b[32] = {1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1,
                 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};
    int counter = 0;
    for(long long int i=0;i<10000000;i++){  //4294967295
        counter += ca2step(i, a, b);
        }

    cout << counter ;
    return 0;
}

答案 9 :(得分:0)

我通过这个帖子,看看问题。但它已经有一段时间了。无论如何,我尝试使用一些按位运算符和openmp。

我的假设:1。处理二进制数                 2.全32位

我用一个int替换了所有数组,因为只包含'0'和'1'的32个宽数组恰好适合一个int(4个字节)。这样做有助于消除一些循环并节省内存访问。

*更新 学习了一些新的技巧,用一些最小的汇编代码进行了更新

#include <iostream>
using namespace std;

#define MASK 0x1F  /*last 5 bits*/

unsigned int toInt(int a[32]){
    int result = 0;
    for(int i = 0; i<32;i++)  
    if(a[i]==1) result |= 1 << (31-i);
    return result;
}

inline unsigned int ror(unsigned int v,unsigned int sh){
//rotate v to the right by sh
    asm("ror %1,%0;" :"=r"(v) : "cI"(sh), "0"(v) );
    return v;
}

unsigned int compute(unsigned int rule, unsigned int target){
    unsigned int t = rol(target,3);
    unsigned int d = 0;
    unsigned int k;
    for(int i=0;i<32;i++){
        k  = ( t & MASK );
        d |= ( (rule>>k) & 1 ) << (31-i) ;
        t  =  rol(t,1);      
    }
    return d;
}

int ca2step (unsigned int rule, unsigned int a, unsigned int b ){
    unsigned int xx = a;
    unsigned int yy = b;

    int tmp;
    unsigned int d,tmpyy;

    for (int j=0; j<19;j++){
        d = compute(rule,yy); 
        tmpyy = xx ^ d ;
        xx = yy;
        yy = tmpyy;
    }
    return ( yy == a || yy == b ) ;  
}    

int main (){

    int a[32] = {0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 
                 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1};
    int b[32] = {1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1,
                 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};
    int counter = 0;
    unsigned int aa = toInt(a);
    unsigned int bb = toInt(b);

    #pragma omp parallel for reduction(+:counter)
    for(unsigned int i=0;i < 0xffffffff ;i++){
        counter += ca2step(i, aa, bb);
    }

    cout << counter <<"\n";

    return 0;
}

编译:

g++ filename.cpp -O3 -fopenmp