我正在读斯科特的书《有效的现代c++》。在第26项中,有一个我在Wandbox上编写的示例:https://wandbox.org/permlink/6DKoDqg4jAjA9ZTB

我想验证好的代码比坏的代码要好多少。但是,性能比较不是我期望的,即使是好的也比坏的要慢。我不知道出什么事了。

为了防止wandbox的代码消失,下面是代码:

#include <iostream>
#include <chrono>
#include <cstdlib>
#include <set>
#include <string>

using namespace std;

std::multiset<std::string> names;

void bad_logAndAdd(const std::string& name) {
    auto now = std::chrono::system_clock::now();
    names.emplace(name);
}
template <typename T>
void good_logAndAdd(T&& name) {
    auto now = std::chrono::system_clock::now();
    names.emplace(std::forward<T>(name));
}

void bad() {
    for (int i=0; i<1000000; i++) {
        string petname("cs");
        bad_logAndAdd(petname);
        bad_logAndAdd(std::string("abc"));
        bad_logAndAdd("dog");
    }

}

void good() {
    for (int i=0; i<1000000; i++) {
        string petname("cs");
        good_logAndAdd(petname);
        good_logAndAdd(std::string("abc"));
        good_logAndAdd("dog");
    }

}

int main()
{
    auto begin = std::chrono::high_resolution_clock::now();
    bad();
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end-begin).count() << std::endl;

    auto begin2 = std::chrono::high_resolution_clock::now();
    good();
    auto end2 = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end2-begin2).count() << std::endl;
}

最佳答案

有几件事使行为的差异难以察觉。

  • 很多时间都花在auto now = std::chrono::system_clock::now();
  • 在内存分配上花费了大量时间(名称全部存储),
  • 在对logAndAdd()的三个调用中,
  • ,其中只有两个可以从移动中受益(petname不能隐式移动),
  • 小字符串不会从移动中受益。

  • 我试图重写(在下面)OP的代码,以最大程度地减少这些因素的影响。
  • 在每次插入
  • 时删除对now()的调用
  • 偏爱许多中等大小的分配,而不是庞大的分配,并考虑进行热身,以避免第一次分配的损失
  • 仅使用可能受益于复制/移动差异的调用
  • 使字符串更长,以使其拷贝昂贵。

  • 在这些情况下,我得到了两种解决方案(gcc 9.1,linux)之间的明显区别。
    bad: 1.23
    good: 1.01
    

    我希望所得到的代码与问题中所要使用的代码相距不远。
    #include <iostream>
    #include <chrono>
    #include <cstdlib>
    #include <set>
    #include <string>
    
    std::multiset<std::string> bad_names;
    std::multiset<std::string> good_names;
    
    void bad_logAndAdd(const std::string& name) {
        // auto now = std::chrono::system_clock::now();
        bad_names.emplace(name);
    }
    template <typename T>
    void good_logAndAdd(T&& name) {
        // auto now = std::chrono::system_clock::now();
        good_names.emplace(std::forward<T>(name));
    }
    
    void bad() {
        for (int i=0; i<2000; i++) {
            // std::string petname("cs_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar");
            // bad_logAndAdd(petname);
            bad_logAndAdd(std::string("abc_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar"));
            bad_logAndAdd("dog_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar");
        }
    
    }
    
    void good() {
        for (int i=0; i<2000; i++) {
            // std::string petname("cs_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar");
            // good_logAndAdd(petname);
            good_logAndAdd(std::string("abc_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar"));
            good_logAndAdd("dog_that_have_been_made_much_longer_in_order_to_prevent_small_string_optimisation_that_makes_copy_and_move_very_similar");
        }
    
    }
    
    int main()
    {
        auto bad_time=std::chrono::high_resolution_clock::duration{};
        auto good_time=std::chrono::high_resolution_clock::duration{};
        for(auto iter=0; iter<1000; ++iter)
        {
          bad_names={};
          auto begin = std::chrono::high_resolution_clock::now();
          bad();
          auto end = std::chrono::high_resolution_clock::now();
          good_names={};
          auto begin2 = std::chrono::high_resolution_clock::now();
          good();
          auto end2 = std::chrono::high_resolution_clock::now();
          if(iter!=0) // ignore warmup
          {
            bad_time+=end-begin;
            good_time+=end2-begin2;
          }
        }
        std::cout << "bad: " << 1e-3*double(std::chrono::duration_cast<std::chrono::milliseconds>(bad_time).count()) << '\n';
        std::cout << "good: " << 1e-3*double(std::chrono::duration_cast<std::chrono::milliseconds>(good_time).count()) << '\n';
        return 0;
    }
    

    10-08 03:09