我需要处理一个文件列表。不应对同一文件重复处理操作。我正在使用的代码是 -
using namespace std;
vector<File*> gInputFileList; //Can contain duplicates, File has member sFilename
map<string, File*> gProcessedFileList; //Using map to avoid linear search costs
void processFile(File* pFile)
{
File* pProcessedFile = gProcessedFileList[pFile->sFilename];
if(pProcessedFile != NULL)
return; //Already processed
foo(pFile); //foo() is the action to do for each file
gProcessedFileList[pFile->sFilename] = pFile;
}
void main()
{
size_t n= gInputFileList.size(); //Using array syntax (iterator syntax also gives identical performance)
for(size_t i=0; i<n; i++){
processFile(gInputFileList[i]);
}
}
代码正常,但是......
我的问题是,当输入大小为1000时,在Windows / Visual Studio 2008 Express上需要30分钟 - 小时 - 小时。对于相同的输入,在Linux / gcc上运行只需40秒!
可能是什么问题?当单独使用时,动作foo()只需很短的时间即可执行。我应该使用像vector :: reserve这样的地图吗?
编辑,附加信息
foo()的作用是: 1.它打开文件 2.将其读入内存 3.关闭文件 4.解析内存中文件的内容 5.它建立一个令牌列表;我正在使用矢量。
每当我打破程序时(使用1000+文件输入集运行程序时):调用堆栈显示程序位于std :: vector add的中间。
答案 0 :(得分:18)
在Microsoft Visual Studio中,访问标准C ++库时存在全局锁定,以防止Debug版本中出现多线程问题。这可能导致大的性能命中。例如,我们的完整测试代码在50分钟内在Linux / gcc上运行,而在Windows VC ++ 2008上则需要5个小时。请注意,使用非调试Visual C ++运行时在发布模式下进行编译时,此性能命中不存在。
答案 1 :(得分:3)
我会像任何性能问题一样接近它。这意味着:分析。顺便说一句,MSVC有一个内置的分析器,所以它可能是熟悉它的好机会。
答案 2 :(得分:2)
我非常强烈怀疑您的性能问题来自STL容器。
尝试消除(注释掉)对foo(pFile)
的调用或任何其他触及文件系统的方法。虽然运行foo(pFile)
一次可能看起来很快,但在1000个不同的文件上运行它(特别是在Windows文件系统上,根据我的经验)可能会慢得多(例如由于文件系统缓存行为。)
您的初始帖子声称BOTH调试和发布版本受到影响。现在你撤回了这个说法。
请注意,在DEBUG版本中:
1000次迭代你可能不会受到上述的影响(至少不在外部循环级别),除非你使用STL /堆很多INSIDE foo()。
答案 3 :(得分:2)
在随机时间使用调试器进入程序,并且堆栈跟踪将告诉您花费时间的位置的可能性非常高。
答案 4 :(得分:1)
如果您遇到的性能问题与map
类有任何关系,我会感到震惊。执行1000次查找和1000次插入应该花费大约微秒的组合时间。什么是foo()
在做什么?
答案 5 :(得分:1)
在不知道其余代码如何适应的情况下,我认为缓存已处理文件的整体想法有点不稳定。
尝试首先从矢量中删除重复项,然后对其进行处理。
答案 6 :(得分:1)
尝试对每个块或主要操作进行注释,以确定哪个部分实际上导致了Linux和Windows中执行时间的差异。我也不认为这是因为STL地图。问题可能在foo()内部。它可能在某些文件操作中,因为在这种情况下,我唯一能想到的是昂贵的。
您可以在操作之间插入clock()调用,以了解执行时间。
答案 7 :(得分:1)
你说当你休息时,你发现自己在vector :: add中。你在我们展示的代码中没有vector :: add,所以我怀疑它在foo
函数中。如果没有看到这些代码,就很难说出是什么了。
您可能无意中创建了Shlemiel the Painter算法。
答案 8 :(得分:1)
如果您放弃地图并对矢量进行分区,则可以稍微改善一下。这意味着重新排序输入文件列表。这也意味着您必须找到一种快速确定文件是否已经处理的方法,可能是通过在File
类中保留一个标志。如果可以对文件列表进行重新排序,并且如果可以将该脏标志存储在File
对象中,则可以将性能从O(n log m)提高到O(n),对于n个文件和m个已处理文件
#include <algorithm>
#include <functional>
// ...
vector<File*>::iterator end(partition(inputfiles.begin(), inputfiles.end(),
not1(mem_fun(&File::is_processed))));
for_each(inputfiles.begin(), end, processFile);
如果您无法重新排序文件列表,或者无法更改File
对象,则可以使用map
切换vector
并隐藏输入中的每个文件文件列表,在同一索引处的第二个向量中带有标志。这将花费你O(n)空间,但会给你O(1)检查脏状态。
vector<File*> processed(inputfiles.size(), 0);
for( vector<File*>::size_type i(0); i != inputfiles.size(); ++i ) {
if( processed[i] != 0 ) return; // O(1)
// ...
processed[i] = inputfiles[i]; // O(1)
}
但是要小心:你正在处理两个指向同一地址的不同指针,这就是两个容器中每对指针的情况。确保一个且只有一个指针拥有指针。
我不希望其中任何一个产生这种性能影响的解决方案,但不过。
答案 9 :(得分:0)
如果您在Linux中完成大部分工作,那么我强烈建议您只在Windows中编译为发布模式。这使得生活变得更加容易,特别是考虑到所有窗口都没有灵活的库处理头痛。