使用 C++ 的 StringBuilder 提升 4350% 的性能(2)

函数ToString()使用std::string::reserve()来实现最小化再分配。下面你可以看到一个性能测试的结果。

函数join()使用std::accumulate(),和一个已经为首个操作数预留内存的自定义函数。

你可能会问,为什么StringBuilder::m_Data用std::list而不是std::vector?除非你有一个用其他容器的好理由,通常都是使用std::vector。

好吧,我(这样做)有两个原因:

1. 字符串总是会附加到一个容器的末尾。std::list允许在不需要内存再分配的情况下这样做;因为vector是使用一个连续的内存块实现的,每用一个就可能导致内存再分配。

2. std::list对顺序存取相当有利,而且在m_Data上所做的唯一存取操作也是顺序的。

你可以建议同时测试这两种实现的性能和内存占用情况,然后选择其中一个。

性能评估

为了测试性能,我从Wikipedia获取一个网页,并将其中一部分内容写死到一个string的vector中。

随后,我编写两个测试函数,第一个在两个循环中使用标准函数clock()并调用std::accumulate()和StringBuilder::ToString(),然后打印结果。

void TestPerformance(const StringBuilder<wchar_t> &tested, const std::vector<std::wstring> &tested2) {
 const int loops = 500;
 clock_t start = clock(); // Give up some accuracy in exchange for platform independence.
 for (int i = 0; i < loops; ++i) {
  std::wstring accumulator;
  std::accumulate(tested2.begin(), tested2.end(), accumulator);
 }
 double secsAccumulate = (double) (clock() - start) / CLOCKS_PER_SEC;

start = clock();
 for (int i = 0; i < loops; ++i) {
  std::wstring result2 = tested.ToString();
 }
 double secsBuilder = (double) (clock() - start) / CLOCKS_PER_SEC;
 using std::cout;
 using std::endl;
 cout << "Accumulate took " << secsAccumulate << " seconds, and ToString() took " << secsBuilder << " seconds."
   << " The relative speed improvement was " << ((secsAccumulate / secsBuilder) - 1) * 100 << "%"
   << endl;
}

第二个则使用更精确的Posix函数clock_gettime(),并测试StringBuilder::Join()。

#ifdef __USE_POSIX199309

// Thanks to <a href="https://www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime/">Guy Rutenberg</a>.
timespec diff(timespec start, timespec end)
{
 timespec temp;
 if ((end.tv_nsec-start.tv_nsec)<0) {
  temp.tv_sec = end.tv_sec-start.tv_sec-1;
  temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
 } else {
  temp.tv_sec = end.tv_sec-start.tv_sec;
  temp.tv_nsec = end.tv_nsec-start.tv_nsec;
 }
 return temp;
}

void AccurateTestPerformance(const StringBuilder<wchar_t> &tested, const std::vector<std::wstring> &tested2) {
 const int loops = 500;
 timespec time1, time2;
 // Don't forget to add -lrt to the g++ linker command line.
 ////////////////
 // Test std::accumulate()
 ////////////////
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time1);
 for (int i = 0; i < loops; ++i) {
  std::wstring accumulator;
  std::accumulate(tested2.begin(), tested2.end(), accumulator);
 }
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time2);
 using std::cout;
 using std::endl;
 timespec tsAccumulate =diff(time1,time2);
 cout << tsAccumulate.tv_sec << ":" <<  tsAccumulate.tv_nsec << endl;
 ////////////////
 // Test ToString()
 ////////////////
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time1);
 for (int i = 0; i < loops; ++i) {
  std::wstring result2 = tested.ToString();
 }
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time2);
 timespec tsToString =diff(time1,time2);
 cout << tsToString.tv_sec << ":" << tsToString.tv_nsec << endl;
 ////////////////
 // Test join()
 ////////////////
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time1);
 for (int i = 0; i < loops; ++i) {
  std::wstring result3 = tested.Join(L",");
 }
 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time2);
 timespec tsJoin =diff(time1,time2);
 cout << tsJoin.tv_sec << ":" << tsJoin.tv_nsec << endl;

////////////////
 // Show results
 ////////////////
 double secsAccumulate = tsAccumulate.tv_sec + tsAccumulate.tv_nsec / 1000000000.0;
 double secsBuilder = tsToString.tv_sec + tsToString.tv_nsec / 1000000000.0;
        double secsJoin = tsJoin.tv_sec + tsJoin.tv_nsec / 1000000000.0;
 cout << "Accurate performance test:" << endl << "    Accumulate took " << secsAccumulate << " seconds, and ToString() took " << secsBuilder << " seconds." << endl
   << "    The relative speed improvement was " << ((secsAccumulate / secsBuilder) - 1) * 100 << "%" << endl <<
            "    Join took " << secsJoin << " seconds."
   << endl;
}
#endif // def __USE_POSIX199309

最后,通过一个main函数调用以上实现的两个函数,将结果显示在控制台,然后执行性能测试:一个用于调试配置。

Debug version screenshot

另一个用于发行版本:

Release version screenshot

看到这百分比没?垃圾邮件的发送量都不能达到这个级别!

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/8b351fbdde9adf5ed80148958d1c60f6.html