使用string vs char数组时有多大的性能差异?

时间:2014-02-21 22:24:56

标签: c++ std

我有以下代码:

char fname[255] = {0}
snprintf(fname, 255, "%s_test_no.%d.txt", baseLocation, i);

VS

std::string fname = baseLocation + "_test_no." + std::to_string(i) + ".txt";

哪一个表现更好?第二个是否涉及临时创作?有没有更好的方法呢?

3 个答案:

答案 0 :(得分:17)

让我们运行数字:

代码(我使用PAPI计时器)

的main.cpp

#include <iostream>
#include <string>
#include <stdio.h>
#include "papi.h"
#include <vector>
#include <cmath>
#define TRIALS 10000000

class Clock
{
  public:
    typedef long_long time;
    time start;
    Clock() : start(now()){}
    void restart(){ start = now(); }
    time usec() const{ return now() - start; }
    time now() const{ return PAPI_get_real_usec(); }
};


int main()
{
  int eventSet = PAPI_NULL;
  PAPI_library_init(PAPI_VER_CURRENT);
  if(PAPI_create_eventset(&eventSet)!=PAPI_OK) 
  {
    std::cerr << "Failed to initialize PAPI event" << std::endl;
    return 1;
  }

  Clock clock;
  std::vector<long_long> usecs;

  const char* baseLocation = "baseLocation";
  //std::string baseLocation = "baseLocation";
  char fname[255] = {};
  for (int i=0;i<TRIALS;++i)
  {
    clock.restart();
    snprintf(fname, 255, "%s_test_no.%d.txt", baseLocation, i);
    //std::string fname = baseLocation + "_test_no." + std::to_string(i) + ".txt";
    usecs.push_back(clock.usec());
  }

  long_long sum = 0;
  for(auto vecIter = usecs.begin(); vecIter != usecs.end(); ++vecIter)
  {
    sum+= *vecIter;
  }

  double average = static_cast<double>(sum)/static_cast<double>(TRIALS);
  std::cout << "Average: " << average << " microseconds" << std::endl;

  //compute variance
  double variance = 0;
  for(auto vecIter = usecs.begin(); vecIter != usecs.end(); ++vecIter)
  {
    variance += (*vecIter - average) * (*vecIter - average);
  }

  variance /= static_cast<double>(TRIALS);
  std::cout << "Variance: " << variance << " microseconds" << std::endl;
  std::cout << "Std. deviation: " << sqrt(variance) << " microseconds" << std::endl;
  double CI = 1.96 * sqrt(variance)/sqrt(static_cast<double>(TRIALS));
  std::cout << "95% CI: " << average-CI << " usecs to " << average+CI << " usecs" << std::endl;  
}

使用评论来获取这种或那种方式。 使用编译行在我的机器上进行了两千万次迭代:

  

g ++ main.cpp -lpapi -DUSE_PAPI -std = c ++ 0x -O3

使用char数组:

Average: 0.240861 microseconds
Variance: 0.196387microseconds
Std. deviation: 0.443156 microseconds
95% CI: 0.240586 usecs to 0.241136 usecs

使用字符串方法:

Average: 0.365933 microseconds
Variance: 0.323581 microseconds
Std. deviation: 0.568842 microseconds
95% CI: 0.365581 usecs to 0.366286 usecs

所以至少在我的机器上有MY代码和我的编译器设置,我在移动到字符串时看到了大约50%的减速。使用以下公式,字符数组比字符串加速34% :

  

((字符串的时间) - (字符数组的时间))/(字符串的时间)

这给出了方法之间的时间差异,单独为字符串的时间百分比。我的原始百分比是正确的;我使用字符数组方法作为参考点,当移动到字符串时显示52%的减速,但我发现它具有误导性。

我将对我如何做错做任何和所有评论:)


编辑:使用GCC 4.8.4编译:

字符串

Average: 0.338876 microseconds
Variance: 0.853823 microseconds
Std. deviation: 0.924026 microseconds
95% CI: 0.338303 usecs to 0.339449 usecs

字符数组

Average: 0.239083 microseconds
Variance: 0.193538 microseconds
Std. deviation: 0.439929 microseconds
95% CI: 0.238811 usecs to 0.239356 usecs

因此字符数组方法保持明显更快,尽管不那么快。在这些测试中,它的速度提高了约29%。

答案 1 :(得分:4)

snprintf()版本几乎肯定会快得多。为什么?仅仅因为没有内存分配。 new运算符非常昂贵,在我的系统上大约250ns - snprintf()在此期间将完成相当多的工作。

这并不是说您应该使用snprintf()方法:您支付的价格是安全的。您提供给snprintf()的固定缓冲区大小很容易出错,而且您绝对需要为缓冲区不够大的情况提供代码。因此,只有在确定这部分代码真正具有性能关键性时才考虑使用snprintf()

如果你有一个符合POSIX-2008标准的系统,你也可以考虑尝试asprintf()而不是snprintf(),它会malloc()为你记忆,给你几乎一样的舒适为C ++ string。至少在我的系统上,malloc()比内置new - 运算符要快得多(不要问我原因)。


编辑:
刚刚看到,您在示例中使用了文件名。如果您关心文件名,请忘记字符串操作的性能!您的代码几乎没有时间花在它们上面。除非您的每秒 100000个字符串操作,否则它们与您的表现无关。

答案 2 :(得分:2)

如果它非常重要,请测量两种解决方案。如果不是,那么您认为最合适的数据来自您拥有的数据,公司/私人编码风格标准等。确保您使用优化的构建[使用与实际生产构建中相同的优化,而不是-O3因为这是最高的,如果您的生产版本使用-O1]

如果你只做一些,我希望这两者都会非常接近。如果你有几百万,可能会有所不同。哪个更快?我猜第二个[1],但这取决于谁编写了snprintf的实现以及谁编写了std::string实现。两者肯定都有可能花费更长的时间,比你想象的功能如何运作(并且可能运行速度超出预期)

[1]因为我使用过printf,并且它不是一个简单的函数,所以花费大量时间来处理格式字符串的各种格式。这不是很有效率(我已经看过glibc中的那些,而且它们并没有明显更好)。 另一方面,std::string函数经常被内联,因为它们是模板实现,这提高了效率。包中的小丑是否可能发生std::string的内存分配。当然,如果某种方式baselocation变得相当大,你可能不希望将它存储为固定大小的本地数组,所以在这种情况下均匀。