C++11 构造函数初始化速度
我运行了一些测试来验证我对构造器速度的先入为主的想法,但结果与我预期的大不相同。我做错什么了吗?我错过了什么? 我使用的是MVS 2015,没有任何优化(但结果总是相同的并不重要) //1-始终比其他系数快 //2-比1稍慢(可忽略!!) //3-比1多花50%的时间 我使用小字符串(8字节)、1K、2K字符串大小运行测试。。 我原以为//2 ctor是最快的,但没想到//3花了这么多时间。 最后一个问题是,我现在做的是合法的吗 更新 这是我写的代码C++11 构造函数初始化速度,c++11,visual-c++,visual-studio-2015,constructor,move-semantics,C++11,Visual C++,Visual Studio 2015,Constructor,Move Semantics,我运行了一些测试来验证我对构造器速度的先入为主的想法,但结果与我预期的大不相同。我做错什么了吗?我错过了什么? 我使用的是MVS 2015,没有任何优化(但结果总是相同的并不重要) //1-始终比其他系数快 //2-比1稍慢(可忽略!!) //3-比1多花50%的时间 我使用小字符串(8字节)、1K、2K字符串大小运行测试。。 我原以为//2 ctor是最快的,但没想到//3花了这么多时间。 最后一个问题是,我现在做的是合法的吗 更新 这是我写的代码 class Person { using u
class Person
{
using ushort = unsigned short;
private:
std::string name_;
ushort age_;
public:
static constexpr size_t nr_ctors = 3;
static const char * const ctor_signature[nr_ctors];
enum class CTOR_1
{ CTOR = 0
};
enum class CTOR_2
{ CTOR = 1
};
enum class CTOR_3
{ CTOR = 2
};
explicit Person(const std::string &name, ushort age,CTOR_1)
{ name_ = name;
age_ = age;
}
explicit Person(const std::string &name, ushort age, CTOR_2) : name_{name},age_{age}
{}
explicit Person(std::string name,ushort age,CTOR_3) : name_{std::move(name)},age_{age}
{}
};
const char * const Person::ctor_signature[Person::nr_ctors] = {"\nexplicit Person(const std::string &name, ushort age,CTOR_1)",
"\nexplicit Person(const std::string &name, ushort age, CTOR_2) : name_{name},age_{age}",
"\nexplicit Person(std::string name,ushort age,CTOR_3) : name_{std::move(name)},age_{age}"};
using mclock = std::chrono::high_resolution_clock;
using time_p_t = std::chrono::time_point<mclock>;
using precision_t = std::chrono::nanoseconds;
#define NR_ITERATIONS (128 * 1024)
template <typename Ty_>
precision_t time_no_heap(const std::string &name)
{
time_p_t t_0;
time_p_t t_1;
t_0 = mclock::now();
Person p = Person{name,66,Ty_::CTOR};
t_1 = mclock::now();
return t_1 - t_0;
}
template <typename Ty_>
precision_t time_with_heap(const std::string &name)
{
time_p_t t_0;
time_p_t t_1;
Person *p_person;
t_0 = mclock::now();
p_person = new Person{ name,66,Ty_::CTOR };
t_1 = mclock::now();
delete p_person;
return t_1 - t_0;
}
void print_statistics(int iterations, size_t str_size, const precision_t(&stats)[2][Person::nr_ctors])
{
std::cout << "\nTotal iterations : "
<< iterations
<< "\nString ize : "
<< str_size
<< std::endl;
for (int i = 0; i < Person::nr_ctors; ++i)
{ std::cout << Person::ctor_signature[i]
<< "\n\t Stack (ms) : "
<< std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(stats[0][i]).count()
<< "\n\t Heap (ms) : "
<< std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(stats[1][i]).count()
<< std::endl;
}
}
int main(int argc, const char *argv[])
{
int iterations;
std::string *p_name;
if (argc != 3 && argc != 1)
{ std::cout << "USAGE [<iterations>K <string size>]" << std::endl;
return -1;
}
else if (argc == 3)
{ iterations = std::atoi(argv[1]) * 1024;
p_name = new std::string(std::atoi(argv[2]), 'x');
}
else
{ iterations = NR_ITERATIONS;
p_name = new std::string{ "Benchmark" };
}
precision_t benchmark [2][Person::nr_ctors]{};
std::cout << "\nUsing string : " << *p_name << ".\nIterating : " << iterations << " times." << std::endl;
for (auto i = iterations; --i >= 0; )
{ //Stack allocation
benchmark[0][(int)Person::CTOR_1::CTOR] += time_no_heap<Person::CTOR_1>(*p_name);
benchmark[0][(int)Person::CTOR_2::CTOR] += time_no_heap<Person::CTOR_2>(*p_name);
benchmark[0][(int)Person::CTOR_3::CTOR] += time_no_heap<Person::CTOR_3>(*p_name);
//Heap allocation
benchmark[1][(int)Person::CTOR_1::CTOR] += time_with_heap<Person::CTOR_1>(*p_name);
benchmark[1][(int)Person::CTOR_2::CTOR] += time_with_heap<Person::CTOR_2>(*p_name);
benchmark[1][(int)Person::CTOR_3::CTOR] += time_with_heap<Person::CTOR_3>(*p_name);
}
print_statistics(iterations,p_name->size(),benchmark);
delete p_name;
return 0;
}
班级人员
{
使用ushort=无符号短;
私人:
std::字符串名称;
乌什特时代;
公众:
静态常量大小系数=3;
静态常量字符*常量字符签名[n个字符];
枚举类CTOR_1
{CTOR=0
};
枚举类CTOR_2
{CTOR=1
};
枚举类CTOR_3
{CTOR=2
};
显式人物(const std::string&name、ushort年龄、CTOR_1)
{name\=名称;
年龄=年龄;
}
显式Person(const std::string&name,ushort age,CTOR_2):name{name},age{age}
{}
显式Person(std::string name,ushort age,CTOR_3):name{std::move(name)},age{age}
{}
};
const char*const Person::ctor_签名[Person::nr_ctors]={“\nexplicit Person(const std::string&name,ushort age,ctor_1)”,
“\nexplicit Person(const std::string&name,ushort age,CTOR_2):name_{name},age_{age}”,
“\nexplicit Person(std::string name,ushort age,CTOR_3):name_{std::move(name)},age_{age}”;
使用mclock=std::chrono::高分辨率时钟;
使用time\u p\u t=std::chrono::time\u点;
使用精度\u t=std::chrono::纳秒;
#定义NR_迭代(128*1024)
模板
精度时间堆(常数std::string和name)
{
时间(t)t(t)0 ;;
时间1;
t_0=mclock::now();
Person p=人名{name,66,Ty_uu::CTOR};
t_1=mclock::now();
返回t_1-t_0;
}
模板
带堆的精度时间(常数std::string和name)
{
时间(t)t(t)0 ;;
时间1;
个人*p_人;
t_0=mclock::now();
p_person=新的人{name,66,Ty_u::CTOR};
t_1=mclock::now();
删除p_人;
返回t_1-t_0;
}
无效打印统计数据(整数迭代、大小、常量精度和统计数据)[2][Person::nr\u ctors])
{
std::cout在第三种情况下,您可以复制一份。相反,请尝试以下方法:
class Person
{ using ushort = unsigned short;
string name_;
ushort age_;
public:
explicit Person(string&& name, ushort age) : name_{ std::move(name) }, age_{ age } //3
{
}
explicit Person(const string &name, ushort age) //1
{
name_ = name;
age_ = age;
}
explicit Person(const string &name,ushort age) : name_{name},age_{age} //2
{
}
};
class Person
{ using ushort = unsigned short;
string name_;
ushort age_;
public:
template<typename T>
explicit Person(T&& name, ushort age) : name_{ std::forward<T>(name) }, age_{ age } //3
{
}
};
R值的使用必须提高性能
或者您可以为aboce r值和l值创建一个构造函数,如下所示:
class Person
{ using ushort = unsigned short;
string name_;
ushort age_;
public:
explicit Person(string&& name, ushort age) : name_{ std::move(name) }, age_{ age } //3
{
}
explicit Person(const string &name, ushort age) //1
{
name_ = name;
age_ = age;
}
explicit Person(const string &name,ushort age) : name_{name},age_{age} //2
{
}
};
class Person
{ using ushort = unsigned short;
string name_;
ushort age_;
public:
template<typename T>
explicit Person(T&& name, ushort age) : name_{ std::forward<T>(name) }, age_{ age } //3
{
}
};
班级人员
{使用ushort=无符号短码;
字符串名称;
乌什特时代;
公众:
模板
显式Person(T&&name,ushort-age):name{std::forward(name)},age{age}//3
{
}
};
没有优化的基准测试毫无意义
作为名称传递的字符串是什么?一个非常重要的因素是字符串的长度,以及它是否会触发SBO(小缓冲区优化)
如果字符串足够短,移动不会比复制快。
另外,您的基准测试是什么样子的?可能是您的基准测试代码有缺陷。我也希望//2是最快的,让我们看看不同的构造函数都做了什么
- //1:name_uu为默认构造,然后为其指定副本
- //2:name_uu是复制构造的
- //3:name是复制构造的,然后name_uu是移动构造的
随着默认构造变得更加昂贵,//1和//2之间的差异将增大
//3是有效的,因为参数名称只存在于构造函数中,所以我们可以通过移动构造名称来窃取内部
Nicolai Josuttis对此有一份来自CppCon 2017的报告。当我尝试对您的代码进行基准测试时,我得到了一些不同的结果。
基准测试在AWS机器池上运行,使用Clang5.0C++17-O3编译
#include <string>
class Person1
{ using ushort = unsigned short;
std::string name_;
ushort age_;
public:
explicit Person1(const std::string &name, ushort age) //1
{ name_ = name;
age_ = age;
}
};
class Person2
{ using ushort = unsigned short;
std::string name_;
ushort age_;
public:
explicit Person2(const std::string &name,ushort age) : name_{name},age_{age} //2
{
}
};
class Person3
{ using ushort = unsigned short;
std::string name_;
ushort age_;
public:
explicit Person3(std::string name, ushort age) : name_{ std::move(name) }, age_{ age } //3
{
}
};
static void CreatePerson1(benchmark::State& state)
{
for (auto _ : state) {
Person1 person("Hello World!!!!!!!!!!!!", 10);
}
}
BENCHMARK(CreatePerson1);
static void CreatePerson2(benchmark::State& state)
{
for (auto _ : state) {
Person2 person("Hello World!!!!!!!!!!!!", 10);
}
}
BENCHMARK(CreatePerson2);
static void CreatePerson3(benchmark::State& state)
{
for (auto _ : state) {
Person3 person("Hello World!!!!!!!!!!!!", 10);
}
}
BENCHMARK(CreatePerson3);
#包括
班级人员1
{使用ushort=无符号短码;
std::字符串名称;
乌什特时代;
公众:
显式Person1(const std::string&name,ushort age)//1
{name\=名称;
年龄=年龄;
}
};
班级人员2
{使用ushort=无符号短码;
std::字符串名称;
乌什特时代;
公众:
显式Person2(const std::string&name,ushort age):name{name},age{age}//2
{
}
};
班级人员3
{使用ushort=无符号短码;
std::字符串名称;
乌什特时代;
公众:
显式Person3(std::string name,ushort age):name{std::move(name)},age{age}//3
{
}
};
静态void CreatePerson1(基准::状态和状态)
{
用于(自动:状态){
人1人(“你好,世界!!!!!!!!!!!”,10);
}
}
基准(CreatePerson1);
静态void CreatePerson2(基准::状态和状态)
{
用于(自动:状态){
人2人(“你好,世界!!!!!!!!!!!”,10);
}
}
基准(CreatePerson2);
静态void CreatePerson3(基准::状态和状态)
{
用于(自动:状态){
人3人(“你好,世界!!!!!!!!!!!”,10);
}
}
基准(CreatePerson3);
对于一个小字符串(8字节),第二个构造函数是最快的,第三个紧随其后
对于较大的字符串(20字节),第三个构造函数是最快的,其次是第二个。请参见此处的结果
没有优化的基准测试是没有意义的。另外,你的基准测试是什么样子的?你传递的字符串是什么(它是否适合SBO)?hi可能在那里运行:你会看到程序集,知道程序实际上在做什么。你也可以测试