为什么在C++中这么慢?

Sir*_*lot 10 c# c++ io

我已经将这个简单的方法从C#转换为C++.它读取路径表并填充整数列表(或整数向量的向量).

路径表中的示例行类似于

0 12 5 16 n
Run Code Online (Sandbox Code Playgroud)

我知道有一般这样做的更好的办法,但现在我只想知道为什么我的C++代码正在采取这么多的时间.例如10分钟而不是C#版本的10秒钟.这是我的C++代码.我猜我做的事情有点严重错误.

//Parses the text path vector into the engine
void Level::PopulatePathVectors(string pathTable)
{
    // Read the file line by line.
    ifstream myFile(pathTable);

        for (unsigned int i = 0; i < nodes.size(); i++)
        {
            pathLookupVectors.push_back(vector<vector<int>>());

            for (unsigned int j = 0; j < nodes.size(); j++)
            {
                string line;

                if (getline(myFile, line)) //Enter if a line is read successfully
                {
                    stringstream ss(line);
                    istream_iterator<int> begin(ss), end;
                    pathLookupVectors[i].push_back(vector<int>(begin, end));
                }
            }
        }
    myFile.close();
}
Run Code Online (Sandbox Code Playgroud)

这是C#版本:

private void PopulatePathLists(string pathList)
{
    // Read the file and display it line by line.
    StreamReader streamReader = new StreamReader(pathList);

    for (int i = 0; i < nodes.Count; i++)
    {
        pathLookupLists.Add(new List<List<int>>());

        for (int j = 0; j < nodes.Count; j++)
        {
            string str = streamReader.ReadLine();
            pathLookupLists[i].Add(new List<int>());

            //For every string (list of ints) - put each one into these lists
            int count = 0;
            string tempString = "";

            while (str[count].ToString() != "n") //While character does not equal null terminator
            {
                if (str[count].ToString() == " ") //Character equals space, set the temp string 
                                                  //as the node index, and move on
                {
                    pathLookupLists[i][j].Add(Convert.ToInt32(tempString));
                    tempString = "";
                }
                else //If characters are adjacent, put them together
                {
                    tempString = tempString + str[count];
                }
                count++;
            }
        }
    }
    streamReader.Close();
}
Run Code Online (Sandbox Code Playgroud)

对不起,这是如此具体,但我很难过.

编辑 - 很多人都说他们已经测试了这段代码,它们只需要几秒钟.我所知道的是,如果我注释掉对此函数的调用,程序会在几秒钟内加载.使用函数调用需要5分钟.几乎一模一样.我真的很难过.问题是什么?

这是它正在使用的PathTable.

编辑 - 我尝试在程序中自行运行该功能,并且花了几秒钟,但我担心我不知道如何解决这个问题.显然这不是代码.会是什么呢?我检查了它被调用的地方,看看是否有多个电话,但没有.它位于游戏关卡的构造函数中,只调用一次.

编辑 - 我知道代码不是最好的,但这不是重点.它可以自行运行 - 大约3秒钟,这对我来说很好.我试图解决的问题是为什么在项目中需要这么长时间.

编辑 - 除了主游戏循环之外,我评论了所有游戏代码.我将该方法放入代码的初始化部分,该部分在启动时运行一次.除了设置一个窗口的几个方法之外,它现在与只有方法的程序几乎相同,只有它仍然需要大约5分钟才能运行.现在我知道它与pathLookupVectors的依赖关系无关.此外,我知道这不是计算机开始写入硬盘驱动器的内存,因为当慢速程序正在运行该方法时,我可以打开另一个Visual Studio实例并在完成的同时运行单个方法程序很快.我意识到问题可能是一些基本的设置,但我没有经历过这样的道歉,如果这确实令人失望地成为原因.我仍然不知道为什么要花这么长时间.

Tim*_*imo 9

我用Very Sleepy(Visual C++ 2010,32位Windows XP)分析了代码.我不知道我的输入数据有多相似,但无论如何这里都是结果:

39%的时间花在了basic_istream :: operator >>上

12%basic_iostream :: basic_iostream

9%的运营商+

8%_Mutex :: Mutex

5%的getline

5%basic_stringbuf :: _ Init

4%locale :: _ Locimp :: _ Addfac

4%vector :: reserve

4%basic_string :: assign

3%运营商删除

2%basic_Streambuf :: basic_streambuf

1%Wcsxfrm

5%其他功能

有些东西似乎来自内联调用,因此有点难以说出它实际来自哪里.但你仍然可以得到这个想法.这里应该做I/O的唯一事情是getline,只需要5%.其余的是流和字符串操作的开销.C++流很慢.

  • @SirYakalot:列表中至少前两个调用来自`stringstream(tempString)>>结果;`.您构造流对象,然后使用operator >>来读取数据.如果你想要更快的字符串到int转换,请使用C函数`atoi`,`atol`,或者像StrTk那样为此目的优化的一些外部库. (12认同)
  • C++流并不慢.这是Dinkumware的实现(与VS一起提供的那个)很慢.将整数写入流涉及**五个锁**,其中四个是*global*.它们都不是标准规定的. (3认同)

Naw*_*waz 7

while代码中的循环看起来非常混乱和冗长,因为它以不需要的方式执行操作:

一个简单而快速的等效代码就是:

int result;
stringstream ss(line);
while ( ss >> result ) //reads all ints untill it encounters non-int
{
    pathLookupVectors[i][j].push_back(result);
}
Run Code Online (Sandbox Code Playgroud)

在C++中,这样的循环也是惯用的.或者代替这个手动循环,您可以编写使用std::copy 1:

std::copy(std::istream_iterator<int>( ss ), 
          std::istream_iterator<int>(), 
          std::back_inserter(pathLookupVectors[i][j]));
Run Code Online (Sandbox Code Playgroud)

这取自@David的评论.

或者甚至更好,如果你这样做,当你push_back的矢量本身:

 if (getline(myFile, line)) //enter if a line is read successfully
 {
   stringstream ss(line);
   std::istream_iterator<int> begin(ss), end;
   pathLookupVectors[i].push_back(vector<int>(begin, end));
 }
Run Code Online (Sandbox Code Playgroud)

完成!


Mig*_*uel 7

根据您的更新,很明显您自己发布的功能不会导致性能问题,因此虽然您可以通过多种方式对其进行优化,但似乎无济于事.

我认为每次运行代码都可以重现这个性能问题,对吗?然后我建议你做以下测试:

  • 如果你是在调试模式下编译你的程序(即没有优化),那么重新编译发布(完全优化,有利于速度),看看是否有所作为.

  • 要检查是否在此可疑函数上花费了额外时间,可以在函数的开头和结尾添加包含时间戳的printf语句.如果这不是一个控制台应用程序,但GUI应用程序和printfs不会去任何地方,那么写入日志文件.如果您使用的是Windows,则可以使用OutputDebugString并使用调试器捕获printfs.如果您使用的是Linux,则可以使用syslog写入系统日志.

  • 使用源代码分析器确定所花费的时间.如果调用此函数与否之间的差异是几分钟,那么分析器肯定会给出发生了什么的线索.如果你在Windows上,那么Very Sleepy是一个不错的选择,如果你在Linux上,你可以使用OProfile.

更新:所以你说发布版本很快.这可能意味着您在此函数中使用的库函数具有较慢的调试实现.STL就是这样知道的.

我确定您需要调试应用程序的其他部分,并且您不希望等待所有这些时间才能在调试模式下完成此功能.此问题的解决方案是在发布模式下构建项目,但以下列方式更改发布配置:

  • 仅对要调试的文件禁用优化(确保优化至少对具有慢速函数的文件保持启用状态).要禁用文件的优化,请在Solution Explorer中选择该文件,右键单击,选择Properties,然后转到Configuration Properties | C/C++/Optimization.查看该页面中的所有项目是如何为Debug构建设置的,并复制Release版本中的所有项目.对您希望调试器可用的所有文件重复此操作.

  • 启用要生成的调试信息(pdb文件).为此,请选择Solution Explorer顶部的Project,右键单击,选择Properties.然后转到Configuration Properties | Linker | Debugging并将Debug构建中的所有设置复制到Release构建中.

通过上述更改,您将能够调试上面配置的发布二进制文件的部分,就像在调试版本中一样.

完成调试后,您需要重新设置所有这些设置.

我希望这有帮助.

  • 我也认为米格尔正在做点什么.我自己经常注意到在错误的代码位置有一个断点(特别是条件断点)可以使它非常慢**.检查所以你在任何地方都没有任何断点,但鉴于你似乎在Visual Studio中没有经验,我猜你没有太多的断点? (2认同)