gpt4 book ai didi

c++ - 当参数作为右值传递时完美转发避免复制?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:36:23 31 4
gpt4 key购买 nike

我正在阅读 Scott 的书 effective modern 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;
}

最佳答案

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

  1. 大部分时间花在 auto now = std::chrono::system_clock::now();,
  2. 大量时间花在内存分配上(名字都存起来了),
  3. 在对 logAndAdd() 的三个调用中,只有两个可以从移动中受益(petname 不可隐式移动),
  4. 小字符串不会从移动中受益。

我试图重写(下面)OP 的代码,以尽量减少这些事情的影响。

  1. 删除每次插入时对 now() 的调用,
  2. 更喜欢大量中等大小的分配,而不是大量分配,并考虑进行预热以避免第一次分配的惩罚,
  3. 只使用可以从复制/移动差异中获益的调用,
  4. 使字符串更长,以使其拷贝成本更高。

在这些情况下,我发现两种解决方案(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;
}

关于c++ - 当参数作为右值传递时完美转发避免复制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57657088/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com