如何针对循环/ if语句优化C ++?

时间:2014-01-25 22:45:57

标签: c++ optimization

有没有让c ++代码运行得更快,我试图优化我的代码中最慢的部分,如:

    void removeTrail ( char floor[][SIZEX],int trail[][SIZEX])
{
    for (int y=1; y < SIZEY-1; y++)                                         
        for (int x=1; x < SIZEX-1; x++)
        {   if (trail [y][x] <= SLIMELIFE && trail [y][x] > 0)  
            {   trail [y][x] --;                                        
                if (trail [y][x] == 0)                                  
                    floor [y][x] = NONE;                                    
            }
        }
}

我在网上找到的大部分指南都是针对更复杂的C ++。

2 个答案:

答案 0 :(得分:1)

这实际上取决于您所寻求的优化类型。在我看来,你正在谈论一个更“低级”的优化,可以通过结合编译标志,通过改变嵌套循环的顺序,改变放置{{1}的位置等技术来实现}语句,在递归方法和迭代方法之间做出决定等等。

但是,最有效的优化是那些针对算法的优化,这意味着您正在改变例程的复杂性,因此通常会将执行时间减少几个数量级。例如,当您决定实施Quicksort而不是Selection Sort时就是这种情况。从O(n ^ 2)到O(n lg n)算法的优化几乎不会受到任何微优化的影响。

在这种特殊情况下,我发现当你达到某个值时,你试图从矩阵中“删除”元素。根据这些值的变化情况,只需跟踪它们到达时间并将其添加到queue以便在那里删除,而不是始终检查整个矩阵,可能会这样做:

if

根据这些值的变化方式(如果它不是简单的trail[y][x]--; // In some part of your code, this happens if (trail[y][x] == 0) { //add for removal removalQueueY[yQueueTail++] = y; removalQueueX[xQueueTail++] = x; } //Then, instead of checking for removal as you currently do: while (yQueueHead < yQueueTail) { //Remove the current element and advance the heads floor[removalQueueY[yQueueHead]][removalQueueX[xQueueHead]] = NONE; yQueueHead++, xQueueHead++; } ),另一个数据结构可能会更有用。您可以尝试使用heaptrail[y][x]--std::set等其他可能性。这一切都归结为算法必须支持的操作,以及哪些数据结构允许您尽可能高效地执行这些操作(考虑内存和执行时间,具体取决于您的优先级和需求)。

答案 1 :(得分:0)

要做的第一件事是打开编译器优化。我所知道的最强大的优化是配置文件引导优化。对于gcc:

1)g ++ -fprofile-generate .... -o my_program

2)运行my_program(典型负载)

3)g ++ -fprofile-use -O3 ... -o optimized_program

使用配置文件O3确实有意义。

接下来就是执行算法优化,就像Renato_Ferreira的回答一样。如果它不适用于您的情况,您可以使用矢量化将性能提高2倍。您的代码看起来可以矢量化:

#include <cassert>
#include <emmintrin.h>
#include <iostream>

#define SIZEX 100 // SIZEX % 4 == 0
#define SIZEY 100
#define SLIMELIFE 100
#define NONE 0xFF

void removeTrail(char floor[][SIZEX], int trail[][SIZEX]) {
    // check if trail is 16 bytes alligned
    assert((((size_t)(&trail[0][0])) & (size_t)0xF) == 0);
    static const int lower_a[] = {0,0,0,0};
    static const int sub_a[] = {1,1,1,1};
    static const int floor_a[] = {1,1,1,1}; // will underflow after decrement
    static const int upper_a[] = {SLIMELIFE, SLIMELIFE, SLIMELIFE, SLIMELIFE};
    __m128i lower_v = *(__m128i*) lower_a;
    __m128i upper_v = *(__m128i*) upper_a;
    __m128i sub_v = *(__m128i*) sub_a;
    __m128i floor_v = *(__m128i*) floor_a;
    for (int i = 0; i < SIZEY; i++) {
        for (int j = 0; j < SIZEX; j += 4) { // only for SIZEX % 4 == 0
            __m128i x = *(__m128i*)(&trail[i][j]);

            __m128i floor_mask = _mm_cmpeq_epi32(x, floor_v); // 32-bit
            floor_mask = _mm_packs_epi32(floor_mask, floor_mask); // now 16-bit
            floor_mask = _mm_packs_epi16(floor_mask, floor_mask); // now 8-bit
            int32_t fl_mask[4];
            *(__m128i*)fl_mask = floor_mask;
            *(int32_t*)(&floor[i][j]) |= fl_mask[0]; 

            __m128i less_mask = _mm_cmplt_epi32(lower_v, x);
            __m128i upper_mask = _mm_cmplt_epi32(x, upper_v);
            __m128i mask = less_mask & upper_mask;
            *(__m128i*)(&trail[i][j]) = _mm_sub_epi32(x, mask & sub_v);
        }
    }   
}

int main()
{   
    int T[SIZEY][SIZEX];
    char F[SIZEY][SIZEX];
    for (int i = 0; i < SIZEY; i++) {
        for (int j = 0; j < SIZEX; j++) {
            F[i][j] = 0x0;
            T[i][j] = j-10;
        }
    }
    removeTrail(F, T);
    for (int j = 0; j < SIZEX; j++) {
        std::cout << (int) F[2][j] << " " << T[2][j] << '\n';
    }
    return 0;
}

看起来它做了它想做的事情。没有ifs和4个迭代值。仅适用于NONE = 0xFF。可以为另一个人做,但很难。