【问题标题】:Effective using std::accumulate with std::string使用 std::accumulate 和 std::string 有效
【发布时间】:2014-05-17 11:39:45
【问题描述】:

我是 STL 算法的粉丝,所以我在工作中经常使用很多 STL 算法。但是,...

考虑以下简单示例: // 编译器:Visual Studio 2010 Sp1。处理器:i5 3300MG。

struct FileInfo
{
   std::string filename_;
   std::string filename()const { return filename_;}
};

//works with 1000 FileInfos,  Elapsed: 127.947 microseconds
std::string sumof_filenames_1(std::vector<FileInfo> const& fv )
{
   std::string s;
   for( std::size_t ix = 0u; ix < fv.size(); ++ix) s += fv[ix].filename();
   return s;
}

//Elapsed: 7430.138 microseconds
std::string sumof_filenames_2(std::vector<FileInfo> const& fv )
{
   struct appender{  
     std::string operator()(std::string const& s, FileInfo const& f)const 
      { return s + f.filename(); } 
   };

   return std::accumulate( std::begin(fv), std::end(fv), std::string(), appender() );
}

//Elapsed: 10729.381 microseconds
std::string sumof_filenames_3(std::vector<FileInfo> const& fv )
{
      struct appender{
           std::string operator()(std::string & s, FileInfo const& f) const
           { return s+= f.filename(); }
      };
      std::accumulate( std::begin(fv), std::end(fv), std::string(), appender() );
}

问:如何使用 STL 算法优化 sum_of_filenames,例如 std::accumulate 或任何其他算法,以及如何实现 appender 函子?

测试:主要功能:

int main()
   {
enum{ N = 1000* 1 };
srand(14324);
std::vector<FileInfo> fv;
fv.reserve(N);
for(std::size_t ix = 0; ix < N; ++ix)
{
    FileInfo f;
    f.m_Filename.resize(  static_cast< int > ( rand() *  256 / 32768 ) + 15 , 'A');
    //for( std::size_t iy = 0; iy < f.m_Filename.size(); ++iy)
    //  f.m_Filename[iy] = static_cast<char>( rand() * 100 / 32768 + 28 );

    fv.push_back( f );
}

LARGE_INTEGER freq, start, stop;
QueryPerformanceFrequency(&freq);

{
    QueryPerformanceCounter(&start);

    std::string s = sumof_filenames_1(fv); 


    QueryPerformanceCounter(&stop);
    double elapsed = (stop.QuadPart - start.QuadPart)* 1000.0 / (double)(freq.QuadPart) * 1000.0;
    printf("Elapsed: %.3lf microseconds\n", elapsed);



    printf("%u\n", s.size());
}


{
    QueryPerformanceCounter(&start);

    std::string s = sumof_filenames_2(fv); 


    QueryPerformanceCounter(&stop);
    double elapsed = (stop.QuadPart - start.QuadPart)* 1000.0 / (double)(freq.QuadPart) * 1000.0;
    printf("Elapsed: %.3lf microseconds\n", elapsed);



    printf("%u\n", s.size());
}




{
    QueryPerformanceCounter(&start);

    std::string s = sumof_filenames_3(fv); 


    QueryPerformanceCounter(&stop);
    double elapsed = (stop.QuadPart - start.QuadPart)* 1000.0 / (double)(freq.QuadPart) * 1000.0;
    printf("Elapsed: %.3lf microseconds\n", elapsed);



    printf("%u\n", s.size());
}

【问题讨论】:

  • 这是一个出于普遍兴趣的问题还是您问的问题,因为您确实有这个问题?因为,如果一个简单的单线比使用 std 算法的版本更快更短,我看不出使用它的意义,即使有更快的解决方案(当然只要它们不比天真的解决方案更快)。
  • 事实上,你甚至可以将其缩短为for (const auto&amp; f: fv) s += f.filename();
  • this answer
  • 仅供参考:除非我遗漏了什么,否则您的时间似乎是以毫秒而不是微秒报告的。 (您报告到微秒精度,但单位是毫秒。)

标签: c++ optimization c++11 stl stl-algorithm


【解决方案1】:

尝试估计以下函数

std::string sumof_filenames_3(std::vector<FileInfo> const& fv )
{
      struct appender{
           std::string & operator()(std::string & s, FileInfo const& f) const
           { return s+= f.filename(); }
      };

      return std::accumulate( std::begin(fv), std::end(fv), std::string(), appender() );
}

并使用 lambda 表达式

std::string sumof_filenames_3(std::vector<FileInfo> const& fv )
{
      return std::accumulate( std::begin(fv), std::end(fv), std::string(),
                              []( std::string &s, FileInfo const& f ) -> std::string &
                              {
                                 return s += f.filename();
                              } ); 
}

同样估计下面的循环

std::string sumof_filenames_1(std::vector<FileInfo> const& fv )
{
   std::string::size_type n = 0;
   for each ( FileInfo const& f in fv ) n +=  f.filename().size();

   std::string s;
   s.reserve( n );

   for( std::size_t ix = 0u; ix < fv.size(); ++ix) s += fv[ix].filename();       

   return s;
}

还对以下方式定义的结构进行相同的估计

struct FileInfo
{
   std::string filename_;
   const std::string & filename()const { return filename_;}
};

【讨论】:

  • accumulate 函数执行init = BinaryOperator(init, *first);,所以我认为在您的运算符中使用+ 而不是+= 更好。
  • @Matt McNabb 如果您将使用 operator +,那么编译器将创建一个临时对象,而如果您将使用 operator +=,则不会创建临时对象。
  • @Matt McNabb 你也错误地编辑了我的帖子。使用 for each 不是错字。 MS VC++ 2010 不支持基于标准范围的 for 语句。
  • MS 多么周到地编写自己的语法而不是检查即将发布的标准内容。
  • 我敢打赌“两个循环,reserve 第一个”将是最快的答案。在此示例中,字符串缓冲区的多次重新分配和副本是最可能的减速原因。
【解决方案2】:

for_each 像这样在我脑海中浮现:

std::string sumof_filenames(std::vector<FileInfo> const& fv )
{
    std::string s;
    std::for_each(std::begin(fv), std::end(fv), [&s](const FileInfo& f){s += f.filename();});
    return s;
}

或者没有兰巴

struct Appender
{
    std::string& m_s;
    Appender(std::string& s):m_s(s){};
    void operator()(const FileInfo& f) const 
        { s += f.filename(); } 
};
std::string sumof_filenames(std::vector<FileInfo> const& fv )
{
    std::string s;
    std::for_each(std::begin(fv), std::end(fv), Appender(s)});
    return s;
}

【讨论】:

  • 不应该void operator()(const FileInfo f) const 通过reference 获取它的论点吗?
  • @Ali:是的,他们绝对应该这样做。我改变了我的帖子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多