0

试图测试移动语义我构建了一个分配一些内存的简单类:

class CTest
{
    private:
        std::size_t size;
        char* buffer;

    public:
        CTest(std::size_t size) : size(size), buffer(nullptr)
        {
            buffer = new char[size];
        }

        ~CTest()
        {
            delete[] buffer;
        }

        CTest(const CTest& other) : size(other.size), buffer(nullptr)
        {
            buffer = new char[size];
        }

        CTest(CTest&& other) : size(other.size), buffer(other.buffer)
        {
            other.buffer = nullptr;
        }
};

正如您所看到的,它是一个非常简单的类,当它通过 ref 复制时,它会分配新的内存(不会复制其内容,只是为了测试)。而移动构造函数只是使内部指针指向参数数据,没有分配新的内存。

使用带有帮助函数的 windows QueryPerformanceCounter() 对我进行基准测试,例如:

template <typename Func>
__int64 Benchmark(Func f, size_t count)
{
    LARGE_INTEGER li = {};
    QueryPerformanceCounter(&li);
    __int64 start = li.QuadPart;

    f(count);

    QueryPerformanceCounter(&li);
    return li.QuadPart - start;
}

非常简单的基准函数,保存开始时间并从结束时间中减去它并返回结果。现在用于测试功能:

void Alloc_Ref(size_t count)
{
    CTest t(1024);
    for(size_t i = 0; i < count; ++i)
        CTest c(t);
}

void Alloc_Move(size_t count)
{
    for(size_t i = 0; i < count; ++i)
        CTest c(CTest(1024));
}

Alloc_Ref 使用一个预先初始化的变量,因此它调用复制构造函数,而 Alloc_Move 仅使用一个临时类,因此它调用移动构造函数。

我这样调用测试:

cout << "Ref: " << Benchmark(Alloc_Ref, 1000000) << " ms." << endl;
cout << "Move: " << Benchmark(Alloc_Move, 1000000) << " ms." << endl;

问题是,Alloc_Move 没有调用移动构造函数,它继续调用复制构造函数,我缺少什么吗?

同样重要的是,当我在 Alloc_Move 上执行此操作时:CTest c(move(CTest(1024))它确实调用了移动构造函数,但它比 Alloc_Ref 慢,还有什么我缺少的吗?

抱歉,这篇长文并提前致谢。

4

2 回答 2

3

问题是,Alloc_Move不是调用移动构造函数,而是继续调用复制构造函数,我缺少什么吗?

你的分析不正确。如果您使用的是 Visual C++ 2010 SP1 编译器,Alloc_Move则既不调用复制构造函数也不调用移动构造函数。声明和初始化

CTest c(CTest(1024));

被转化为

CTest c(1024);

c直接调用构造函数初始化CTest(std::size_t size)。不会创建或销毁临时CTest对象。


Alloc_Move在我的 Visual Studio 2010 32 位 VM 上,我发现你的和函数之间的性能差异大约为 20% Alloc_Ref(后者更快)。我简要浏览了生成的程序集,但没有看到任何明显的差异原因。使用 Visual C++ 11 Beta 编译器(适用于 x86),Alloc_Move速度远远超过两倍Alloc_Ref(我没有进一步调查,也不知道原因是什么)。

[另请注意,您的测量单位是错误的: QueryPerformanceCounter不返回毫秒;它返回滴答声。您需要调用QueryPerformanceFrequency以获取每秒的滴答数,然后相应地划分您的测量值。]

于 2012-04-16T20:42:37.423 回答
-2

为了利用移动语义,您需要显式使用右值引用。为了与旧代码兼容,编译器永远不会为您隐式转换为右值引用;您需要使用std::move或显式转换。

Alloc_Move的速度很可能更慢,因为CTest(1024)每次你都在构造一个新的构造c,而 asAlloc_Ref只构造一个CTest(1024)并重用它。

于 2012-04-16T19:30:25.893 回答