我该怎么办这个瓶颈

时间:2015-06-26 15:26:30

标签: c++ algorithm for-loop optimization graph-algorithm

我刚刚找到了一种优化代码算法的方法,从50分钟到15分钟,但这部分需要14分钟。它将成为更大型模型化系统的一部分,因此我无法承受长期运行。由于我必须比较我的矢量的所有值,它具有大约100,000个值(100亿比较),我想知道是否有一种方法来优化代码。

toAdd

我仍然是C ++的新手,所以我不习惯语言可以提供的所有可能性以及使功能比另一种更快的区别。

5 个答案:

答案 0 :(得分:2)

下面示例代码中的函数Connection_S_optimized可以在3秒内处理100000 Coor的数组,而原始代码在55秒内处理它。

以下代码的主要思想是对端点进行排序并将其放入多图中。节点1坐标和节点2坐标都放在同一个地图中,注意它是哪个。

然后,通过一组相同的端点,我们填充每个Coor的数组C1和C2。

请注意,优化版本的工作方式与原始矢量不同,x1,y1与x2,y2相同。

正如有人已经指出的那样,比较双打的平等可能是危险的,因此,你可以调整函数is_same_coordinate进行近似比较。

此代码可以使用SEEMS,但使用它需要您自担风险或当然。

#include <vector>
#include <map>
#include <algorithm>
#include <time.h>
#include <iostream>

struct Coor
{
    double x1; double y1; //Coordinate of Node 1
    double x2; double y2; //Coordinate of Node 2
    std::vector<int> C1; //Index of the edge connected to Node 1
    std::vector<int> C2; //Index of the edge connected to Node 2
};

bool is_same_coordinate(std::pair<double, double> e1, std::pair<double, double> e2)
{
    return (e1.first == e2.first) && (e1.second == e2.second);
}

std::vector<Coor> Connection_S(std::vector<Coor> Noeuds)
{
    size_t N = Noeuds.size();

    for (size_t i = 0; i < N; ++i)
    {
        for (size_t j = 0; j < N; ++j)
        {
            if (i == j)
            {
                continue;
            }
            if ((Noeuds[i].x1 == Noeuds[j].x1 && Noeuds[i].y1 == Noeuds[j].y1) || (Noeuds[i].x1 == Noeuds[j].x2 && Noeuds[i].y1 == Noeuds[j].y2))
            {
                Noeuds[i].C1.push_back(j);
            }
            if ((Noeuds[i].x2 == Noeuds[j].x1 && Noeuds[i].y2 == Noeuds[j].y1) || (Noeuds[i].x2 == Noeuds[j].x2 && Noeuds[i].y2 == Noeuds[j].y2))
            {
                Noeuds[i].C2.push_back(j);
            }
        }
    }
    return Noeuds;
}


void Connection_S_optimized(std::vector<Coor>& Noeuds)
{
    // A map of an endpoint coordinates to the information about this enpoint <is it the Node 1 (x1, y1), index in Noeuds>
    std::multimap<std::pair<double, double>, std::pair<bool, size_t>> node_index;

    for (size_t i = 0; i < Noeuds.size(); i++)
    {
        Coor& c = Noeuds[i];
        node_index.insert(std::make_pair(std::pair<double, double>(c.x1, c.y1), std::pair<bool, size_t>(true, i)));
        node_index.insert(std::make_pair(std::pair<double, double>(c.x2, c.y2), std::pair<bool, size_t>(false, i)));
    }
    auto s_representative_it = node_index.begin();
    for (auto s_it = node_index.begin();; s_it++)
    {
        if (s_it == node_index.end() || !is_same_coordinate(s_representative_it->first, s_it->first))
        {
            auto start = s_representative_it;
            auto end = s_it;
            auto current = s_representative_it;
            while (current != end)
            {
                bool is_node_1 = current->second.first;
                Coor& current_coor = Noeuds[current->second.second];
                auto it = start;
                while (it != end)
                {
                    if (it != current)
                    {
                        if (is_node_1)
                        {
                            current_coor.C1.push_back(it->second.second);
                        }
                        else
                        {
                            current_coor.C2.push_back(it->second.second);
                        }
                    }
                    it++;
                }
                current++;
            }
            if (s_it == node_index.end())
            {
                break;
            }
            s_representative_it = s_it;
        }
    }
}

const size_t NUM_COORS = 100000;

void generate_sample_set(std::vector<Coor>& Noeuds)
{
    Coor c;
    size_t degenerated = 0;
    for (size_t i = 0; i < NUM_COORS + degenerated; i++)
    {
        c.x1 = i % 23;
        c.x2 = i % 13;
        c.y1 = i % 5;
        c.y2 = i % 17;
        if (is_same_coordinate(std::make_pair(c.x1, c.y1), std::make_pair(c.x2, c.y2)))
        {
            degenerated++;
            continue;
        }
        Noeuds.push_back(Coor(c));
    }
}

int main(int argc, char** argv)
{
    std::vector<Coor> Noeuds_input;

    generate_sample_set(Noeuds_input);

    std::vector<Coor> Noeuds_original = Noeuds_input;
    std::vector<Coor> Noeuds_optimized = Noeuds_input;

    double time_original = clock();
    Noeuds_original = Connection_S(Noeuds_original);
    time_original = (clock() - time_original) / CLOCKS_PER_SEC;

    double time_optimized = clock();
    Connection_S_optimized(Noeuds_optimized);
    time_optimized = (clock() - time_optimized) / CLOCKS_PER_SEC;

    for (size_t i = 0; i < std::min(Noeuds_input.size(), 100u); i++)
    {
        std::cout << i << ": " << Noeuds_original[i].C1.size() << "," << Noeuds_original[i].C2.size()
            << " vs " << Noeuds_optimized[i].C1.size() << "," << Noeuds_optimized[i].C2.size() << std::endl;
    }

    std::cout << "Processing time for " << NUM_COORS << " items (in seconds):" << std::endl;
    std::cout << "  original: " << time_original << std::endl;
    std::cout << " optimized: " << time_optimized << std::endl;

    return 0;
}

答案 1 :(得分:2)

编译器优化设置

首先打印汇编语言列表 接下来,将优化级别设置为High for Speed;重新编译。
将优化组件与非优化组件进行比较。

预加载变量

您可以通过预加载您正在比较的值来节省一些费用。 (注意:编译器可能已经这样做了;请检查您的本地汇编语言是否真实。)
示例:

const double ni_x1(Nodes[i].x1);
const double ni_x2(Nodes[i].x2);
const double nj_y1(Nodes[i].y1);
const double nj_y2(Nodes[i].y2);

if (((ni_x1 == nj_x1) && (ni_y1 == nj_y1))
// ...

这里的优化技术是允许处理器将数据预取到其数据高速缓存中。

减少分支指令

分支指令比数据指令花费更多时间来执行处理器。因此,如果可能的话,消除它们 (某些处理器有足够的缓存来将循环加载到指令缓存中而无需重新加载。在任何情况下,处理器仍然需要执行一些额外的逻辑,这比处理数据指令需要更多的时间。)

你可以使用一些布尔代数。再次,检查汇编语言,看看你是否获得了任何速度。 示例:

bool is_equal = false;
is_equal = (ni_x1 == nj_x1);
is_equal = is_equal && (ni_y1 == nj_y1);

如果你的处理器有这个,上面的代码可能允许编译器生成条件汇编指令。希望编译器可以生成连续的数据指令。

不动点算术

另一种选择是使用定点算术。这将允许整数算术运算,这通常比浮点运算更快。

例如,给定的升数量,有可能有3.141升。如果该值表示为毫升,则该值将为积分:3141。

优点:更好的准确性和平等性。例如,对于32位处理器,您可以有32位“尾数”,而浮点可能只有24位“尾数”,因为有些位是为符号和指数保留的。

答案 2 :(得分:1)

你可以在循环之前调整Noeuds [i]向量的大小。这将改善内存管理和性能。 通过引用传递结构Noeuds。现在它正在复制它。这需要时间

更改Connection_S(std::vector<Coor> Noeuds)Connection_S(std::vector<Coor> &Noeuds) 当您通过参考传递时,您不必返回它。 原件将直接更新。

在旁注:比较双打==并不能给你总是相同的结果。所以这很危险。

答案 3 :(得分:1)

你可以做一些事情:

  1. 预先对整个向量进行排序此操作与您现在正在进行的操作相比为O(nlogn),即O(N * N)。你可以找到一种更有效的填充C1 / C2的方法。
  2. 您也可以尝试一次进行多项比较; j将增加2,4 ..等。这将改善您的缓存局部性,并应生成更紧凑的代码。

答案 4 :(得分:0)

旁注:&#34; Node&#34;的复数形式是&#34;节点&#34; ; - )

std::vector内部使用动态分配的数组,这意味着push_back操作,如here所述,可能需要重新分配内存,然后复制已经存在的所有数据向量。根据矢量的大小,这可能是一个相当昂贵的操作。

相反,请考虑使用其他一些容器。 std::list在最后添加元素方面效率很高,但不支持随机访问。或者,您可能希望使用std::deque,它不保证所有元素都在连续存储中(如矢量那样),但仍允许相当有效的随机访问。