C++11 构造函数初始化速度

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

我运行了一些测试来验证我对构造器速度的先入为主的想法,但结果与我预期的大不相同。我做错什么了吗?我错过了什么? 我使用的是MVS 2015,没有任何优化(但结果总是相同的并不重要)

//1-始终比其他系数快

//2-比1稍慢(可忽略!!)

//3-比1多花50%的时间

我使用小字符串(8字节)、1K、2K字符串大小运行测试。。 我原以为//2 ctor是最快的,但没想到//3花了这么多时间。 最后一个问题是,我现在做的是合法的吗

更新 这是我写的代码

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可能在那里运行:你会看到程序集,知道程序实际上在做什么。你也可以测试