我有一个C ++程序,它将读取二进制文件中的数据,最初我将数据存储在std::vector<char*> data
中。我已经更改了我的代码,以便我现在使用字符串而不是char *,以便std::vector<std::string> data
。我必须做的一些更改是从strcmp
更改为compare
。
但是我看到我的执行时间急剧增加。对于示例文件,当我使用char *时需要0.38s,在转换为字符串后,我的Linux机器上花了1.72s。我在Windows机器上观察到类似的问题,执行时间从0.59s增加到1.05s。
我相信这个功能导致速度减慢。它是转换器类的一部分,请注意在变量名末尾用_
指定的私有变量。我显然在这里遇到了内存问题,并且陷入了C和C ++代码之间。 我希望这是C ++代码,所以我更新了底部的代码。
我在其他功能中也多次访问ids_
和names_
,因此访问速度非常重要。 通过使用创建map
而不是两个单独的向量,我已经能够使用更稳定的C ++代码实现更快的速度。谢谢大家!
2515 ABC 23.5 32 -99 1875.7 1
1676 XYZ 12.5 31 -97 530.82 2
279 FOO 45.5 31 -96 530.8 3
void converter::updateNewList(){
FILE* NewList;
char lineBuffer[100];
char* id = 0;
char* name = 0;
int l = 0;
int n;
NewList = fopen("NewList.txt","r");
if (NewList == NULL){
std::cerr << "Error in reading NewList.txt\n";
exit(EXIT_FAILURE);
}
while(!feof(NewList)){
fgets (lineBuffer , 100 , NewList); // Read line
l = 0;
while (!isspace(lineBuffer[l])){
l = l + 1;
}
id = new char[l];
switch (l){
case 1:
n = sprintf (id, "%c", lineBuffer[0]);
break;
case 2:
n = sprintf (id, "%c%c", lineBuffer[0], lineBuffer[1]);
break;
case 3:
n = sprintf (id, "%c%c%c", lineBuffer[0], lineBuffer[1], lineBuffer[2]);
break;
case 4:
n = sprintf (id, "%c%c%c%c", lineBuffer[0], lineBuffer[1], lineBuffer[2],lineBuffer[3]);
break;
default:
n = -1;
break;
}
if (n < 0){
std::cerr << "Error in processing ids from NewList.txt\n";
exit(EXIT_FAILURE);
}
l = l + 1;
int s = l;
while (!isspace(lineBuffer[l])){
l = l + 1;
}
name = new char[l-s];
switch (l-s){
case 2:
n = sprintf (name, "%c%c", lineBuffer[s+0], lineBuffer[s+1]);
break;
case 3:
n = sprintf (name, "%c%c%c", lineBuffer[s+0], lineBuffer[s+1], lineBuffer[s+2]);
break;
case 4:
n = sprintf (name, "%c%c%c%c", lineBuffer[s+0], lineBuffer[s+1], lineBuffer[s+2],lineBuffer[s+3]);
break;
default:
n = -1;
break;
}
if (n < 0){
std::cerr << "Error in processing short name from NewList.txt\n";
exit(EXIT_FAILURE);
}
ids_.push_back ( std::string(id) );
names_.push_back(std::string(name));
}
bool isFound = false;
for (unsigned int i = 0; i < siteNames_.size(); i ++) {
isFound = false;
for (unsigned int j = 0; j < names_.size(); j ++) {
if (siteNames_[i].compare(names_[j]) == 0){
isFound = true;
}
}
}
fclose(NewList);
delete [] id;
delete [] name;
}
void converter::updateNewList(){
std::ifstream NewList ("NewList.txt");
while(NewList.good()){
unsigned int id (0);
std::string name;
// get the ID and name
NewList >> id >> name;
// ignore the rest of the line
NewList.ignore( std::numeric_limits<std::streamsize>::max(), '\n');
info_.insert(std::pair<std::string, unsigned int>(name,id));
}
NewList.close();
}
更新:跟进问题:Bottleneck from comparing strings并感谢您提供非常有用的帮助!我将来不会犯这些错误!
答案 0 :(得分:7)
我猜它应该与矢量&lt; string&gt;的性能相关联
A std::vector
与内部连续数组一起工作,这意味着一旦数组已满,它需要创建另一个更大的数组,并逐个复制字符串,这意味着复制构造和销毁具有相同内容的字符串,这会适得其反......
要轻松确认,请使用std::vector<std::string *>
并查看效果是否存在差异。
如果是这种情况,你可以做以下四件事之一:
reserve()
在内部数组中保留足够的空间,以避免无用的重新分配。std::deque
,它几乎像向量std::list
(不允许您随意访问其项目)注意:我假设你的strings \ char *被创建一次,而不是修改(通过realloc,append等)。
如果上述想法不够,那么......
字符串对象的内部缓冲区的分配类似于char *
的malloc,因此您应该看到两者之间很少或没有区别。
现在,如果你的char *
实际上是char[SOME_CONSTANT_SIZE]
,那么你就避开了malloc(因此,它会比std :: string更快)。
阅读更新的代码后,我发现以下问题。
reserve()
其他评论:
int
来表示大小和长度。至少,使用size_t
。在64位中,size_t将变为64位,而int将保持32位,因此您的代码不是64位就绪(另一方面,我看到传入8个Go字符串的情况很少......但是,更好的是正确的......)两个(所谓的C和C ++)代码是不同的。 “C代码”需要长度小于5的ID和名称,否则程序存在错误。 “C ++代码”没有这样的限制。尽管如此,如果确认名称和ID总是小于5个字符,这个限制仍然可以进行大规模优化。
答案 1 :(得分:3)
在开始填充之前,将矢量调整为足够大的大小。或者,使用指向字符串而不是字符串的指针。
事情是每次向量自动调整大小时都会复制字符串。对于诸如指针之类的小对象,这几乎没有任何成本,但对于字符串,整个字符串将被完整复制。
ID和名称应该是string
而不是char*
,并且可以像这样进行初始化(假设您仍然使用string
代替string*
):
id = string(lineBuffer, lineBuffer + l);
...
name = string(lineBuffer + s, lineBuffer + s + l);
...
ids_.push_back(id);
names_.push_back(name);
答案 2 :(得分:3)
在修复之前确保它是瓶颈。否则你就是在浪费时间。此外,这种优化是微优化。如果你在C ++中进行微优化,那么考虑使用裸C。
答案 3 :(得分:2)
除了std :: string,这是一个C程序。
尝试使用fstream,并使用探查器检测瓶颈。
答案 4 :(得分:2)
您可以尝试reserve
多个vector
值,以减少分配数量(这是昂贵的),如同Dialecticus(可能来自古罗马人?)。
但有些东西可能值得一些观察:如何存储文件中的字符串,是否执行连接等...
在C中,字符串(每个人不存在 - 他们没有像STL这样的库中的容器)需要更多的工作来处理,但至少我们知道在处理它们时会发生什么。在STL中,每个方便的操作(意味着需要较少的程序员工作)实际上可能需要在string
类中的场景后面进行大量操作,具体取决于您使用它的方式。
因此,虽然分配/释放是一个代价高昂的过程,但其余的逻辑,特别是字符串过程,也可能/应该被视为。
答案 5 :(得分:2)
我认为这里的主要问题是你的字符串版本是两次复制东西 - 首先是动态分配的char[]
name
和id
,然后是std::string
s,而你的vector<char *>
版本可能不会这样做。要使字符串版本更快,您需要直接读取字符串并删除所有冗余副本
答案 6 :(得分:2)
void converter::updateNewList(){
std::ifstream NewList ("NewList.txt");
while(NewList.good()){
int id (0);
std::string name;
// get the ID and name
NewList >> id >> name;
// ignore the rest of the line
NewList.ignore( numeric_limits<streamsize>::max(), '\n');
ids_.push_back (id);
names_.push_back(name);
}
NewList.close();
}
无需手动进行空格标记。
此外,您可能会发现此网站是一个有用的参考: http://www.cplusplus.com/reference/iostream/ifstream/
答案 7 :(得分:1)
您可以使用分析器找出代码消耗最多时间的位置。例如,如果您使用gcc,则可以使用-pg编译程序。运行它时,它会将分析结果保存在文件中。您可以在二进制文件上运行gprof以获得人类可读的结果。一旦你知道大部分时间消耗在哪里,你可以发布这段代码以获得更多问题。