6

我发现标准字符串添加非常慢,所以我正在寻找一些可以加快我拥有的一些代码的技巧/技巧。

我的代码基本上结构如下:

inline void add_to_string(string data, string &added_data) {
   if(added_data.length()<1) added_data = added_data + "{";
   added_data = added_data+data;
}

int main()
{
   int some_int = 100;
   float some_float = 100.0;
   string some_string = "test";

   string added_data;
   added_data.reserve(1000*64);

   for(int ii=0;ii<1000;ii++)
   {
      //variables manipulated here
      some_int = ii;  
      some_float += ii;
      some_string.assign(ii%20,'A');
      //then we concatenate the strings!
      stringstream fragment;
      fragment<<some_int <<","<<some_float<<","<<some_string;
      add_to_string(fragment.str(),added_data);
   }
   return;
}

做一些基本的分析,我发现在 for 循环中使用了大量的时间。我能做些什么来显着加快速度吗?使用 c 字符串而不是 c++ 字符串会有所帮助吗?

4

7 回答 7

5

字符串添加不是您面临的问题。std::stringstream 由于它的设计而被称为慢。在 for 循环的每次迭代中,stringstream 至少负责 2 次分配和 2 次删除。这 4 种操作中的每一种操作的成本都可能高于字符串添加的成本。

分析以下内容并测量差异:

std::string stringBuffer;
for(int ii=0;ii<1000;ii++)
{
  //variables manipulated here
  some_int = ii;  
  some_float += ii;
  some_string.assign(ii%20,'A');
  //then we concatenate the strings!
  char buffer[128];
  sprintf(buffer, "%i,%f,%s",some_int,some_float,some_string.c_str());
  stringBuffer = buffer;
  add_to_string(stringBuffer ,added_data);
}

理想情况下,将 sprintf 替换为 _snprintf 或编译器支持的等效项。

根据经验,默认情况下使用 stringstream 进行格式化,并在性能很重要时切换到更快、更不安全的函数,如 sprintf、itoa 等。

编辑:那个,以及 didierc 所说的: added_data += data;

于 2012-12-13T01:17:10.890 回答
4

如果您不在add_to_string循环中调用,您可以节省大量字符串操作。

我相信这也是一样的(尽管我不是 C++ 专家,也不知道具体是做什么stringstream的):

stringstream fragment;
for(int ii=0;ii<1000;ii++)
{
  //variables manipulated here
  some_int = ii;  
  some_float += ii;
  some_string.assign(ii%20,'A');
  //then we concatenate the strings!
   fragment<<some_int<<","<<some_float<<","<<some_string;
}

// inlined add_to_string call without the if-statement ;)
added_data = "{" + fragment.str();
于 2012-12-13T01:15:05.663 回答
3

我看到您在 上使用了 reserve 方法added_data,这应该有助于避免在字符串增长时对其进行多次重新分配。

您还应该+=尽可能使用字符串运算符:

added_data += data;

added_data我认为通过避免在进行连接时在临时字符串中来回进行不必要的复制,以上内容应该可以节省大量时间。

+=运算符是该string::append方法的一个更简单的版本,它只是data直接在added_data. 由于您进行了预留,因此仅该操作就应该非常快(几乎相当于 strcpy)。

但是,当您已经在使用字符串流来处理输入时,为什么还要经历这一切呢?一开始就把它全放在那里!

stringstream 类确实效率不高。

如有必要,您可以查看stringstream 类以获取有关如何使用它的更多信息,但您使用字符串作为缓冲区的解决方案似乎避免了该类速度问题。

无论如何,除非您真的知道自己在做什么,否则不要尝试在纯 C 中重新实现速度关键代码。其他一些 SO 帖子支持这样做的想法,但我认为最好(阅读更安全)尽可能多地依赖标准库,这将随着时间的推移而得到增强,并照顾你(或我)的许多极端情况) 不会想到的。如果您的输入数据格式是一成不变的,那么您可能会开始考虑走这条路,否则就是过早的优化。

于 2012-12-13T01:27:12.023 回答
2

如果您added_data以 a开头"{",您将能够if从您的add_to_string方法中删除 :if当字符串为空时,只执行一次,因此您不妨立即将其设为非空。

此外,您add_to_string制作了一份data; 这不是必需的,因为它不会被修改。接受data引用const应该会为您加快速度。

最后,更改added_datafrom stringtosstream应该可以让您在循环中附加到它,而无需在循环sstream的每次迭代中创建、复制和丢弃中介。

于 2012-12-13T01:10:51.097 回答
2

请查看LLVM 中使用的Twine 。

Twine 是一种绳索,它使用二叉树表示连接的字符串,其中字符串是节点的前序。由于 Twine 在使用其结果时可以有效地呈现到缓冲区中,因此它避免了为中间字符串结果生成临时值的成本——尤其是在从不需要 Twine 结果的情况下。通过显式跟踪叶节点的类型,我们还可以避免为转换操作创建临时字符串(例如将整数附加到字符串)。

它可能有助于解决您的问题。

于 2012-12-13T01:44:21.500 回答
0

这种方法怎么样?

这是 MSVC 2010 报告的 DevPartner。

在此处输入图像描述

于 2012-12-13T02:07:03.350 回答
-4

字符串 newstring = stringA & stringB;

我不认为字符串很慢,它的转换会使它变慢,也许你的编译器可能会检查变量类型是否不匹配。

于 2012-12-13T01:10:41.597 回答