在搜索最大平方算法中优化功能

时间:2018-08-21 10:15:20

标签: c optimization

我是优化的新手,而且我正在一场友好的竞赛中编写最快的算法,以在有障碍物的领域中找到一个正方形。我认为我的算法相当快(在密度为70%的10k * 10k网格中为2.07s),但我想使其更快。我最常用的功能是在正方形中找到给定坐标的最大大小。这是:

int     max_size(int i, int j, int **tab, int offset)
{
int a;

a = offset;
if (i > 0 && j > 0)
{
    while (i + a < g_ymax && j + a < g_xmax && tab[i + a][j + a] - tab[i + a][j - 1] - tab[i - 1][j + a] + tab[i - 1][j - 1] == 0)
        a++;
    return (a);
}
else if (i > 0 && j == 0)
{
    while (i + a < g_ymax && j + a < g_xmax && tab[i + a][j + a] - tab[i - 1][j + a] == 0)
        a++;
    return (a);
}
else if (i == 0 && j > 0)
{
    while (i + a < g_ymax && j + a < g_xmax && tab[i + a][j + a] - tab[i + a][j - 1] == 0)
        a++;
    return (a);
}
else
{
    while (i + a < g_ymax && j + a < g_xmax && tab[i + a][j + a] == 0)
        a++;
    return (a);
}
}

所以这很丑陋,我想对其进行优化。会使其内联函数有所帮助吗?我尝试运行一些测试,但没有发现太大差异。

4 个答案:

答案 0 :(得分:2)

首先,在不考虑特定系统的情况下谈论性能或手动优化没有多大意义。通常,为了比该系统的编译器更好地进行优化,您必须是该特定系统的专家。

但是,假设这是一些主流的高端系统,例如x86,PowerPC或ARM。到目前为止,对于性能而言最重要的是网格如何存储在内存中。

您拥有int **tab并将其视为2D数组,这是代码异味。这最有可能意味着tab指向某个基于指针的查找表,该表可能已分配在堆上。访问此查找表将在具有数据高速缓存存储器的系统上造成严重的性能损失,因为数据分段将阻止有效的数据高速缓存利用率。请参见Correctly allocating multi-dimensional arrays,以获取有关如何从根本上改善性能的建议。

将网格移动到连续内存后,下一步就是确保以最右边的维是最频繁更改维的方式对它进行迭代。也就是说,given array[i][j]确保进行迭代,以使j是变化最频繁的迭代器。这也与缓存的使用以及可能的对齐方式有关。

例如,使用真值表,可以通过减少比较次数来提高性能。比较次数越少,分支预测越好。

内联函数的确可能会稍微提高性能,尽管编译器应该能够在那里做出更好的决策,因为内联是以可执行文件大小和内存使用为代价的。

如果最大为10k * 10k,则可以通过将int换为uint_fast16_t来获得较小的性能提升,尽管这主要是针对8位和16位CPU的优化。

答案 1 :(得分:0)

对此我不确定,它可能有助于编译器将这两个值保留在寄存器中:

int max_size(int i, int j, int **tab, int offset)
{
   int a;
   const int ymax = g_ymax;
   const int xmax = g_xmax;

   // Use ymax and xmax in the rest of code.

如果g_ymaxg_xmax是常量,如:

   static const int g_xmax = 1234;

那么这将无济于事。

答案 2 :(得分:0)

在函数中使用局部变量可简化while循环中的条件。例如,您的第一个循环可以简化为:

int   Limit;
int * C1=tab[i-1];
int   C2=C1[j - 1];

if (g_ymax-i < g_xmax-j)
{
  Limit = g_ymax-i;
}
else
{
  Limit = g_xmax-j;
}

while ((a < Limit) && (tab[i + a][j + a] - tab[i + a][j - 1] - C1[j + a] + C2 == 0) )
{
  a++;
}

return (a);

取消引用指针也会消耗时钟周期。这就是为什么我在数组的索引为常数时使用C1和C2的原因。

答案 3 :(得分:-1)

我们比较'for(;;)'和'while(1)',我阅读了相应的汇编语言代码:

'for(;;)',其汇编语言没有判断语句。

'while(1)',其汇编语言有一种判断,判断1需要继续循环。