我正在读斯科特的书《有效的现代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;
}