make_unique <t>()和unique_ptr <t>(新T)

时间:2018-03-02 07:34:47

标签: c++14

我认为以下两个是相同的:(使用C ++ 14)

std::unique_ptr<char[]> buf(std::make_unique<char[]>(size));
std::unique_ptr<char[]> buf(new char[size]);

但是当我编译这两个并测量每个实际花费的时间时,结果是非常意外的。这是我的测试代码:

#include <stdint.h>
#include <memory>
#include <chrono>

#include <sys/time.h>
#include <sys/types.h>

class Elapsed
{
public:
    Elapsed()
    {
        start();
    }
    int64_t getElapsedMicro()
    {
        std::chrono::steady_clock::time_point end(std::chrono::steady_clock::now());
        return std::chrono::duration_cast<std::chrono::microseconds>(end - mStart).count();
    }
    void start(void)
    {
        mStart = std::chrono::steady_clock::now();
    }

private:
    std::chrono::steady_clock::time_point mStart;
};

static void loop(char *p, size_t aSize)
{
    Elapsed sElapsed;
    for (size_t i = 0; i < aSize; i++)
    {
        if (*(p + i) == 0)
        {
            *(p + i) = i % 128;
        }
        else
        {
            *(p + i) = i % 128;
        }
    }
    printf("%ld usec to loop buffer\n", sElapsed.getElapsedMicro());
}

int32_t main(int32_t aArgc, char *aArgv[])
{
    int32_t sChoice = aArgv[2][0] - '0';
    size_t sSize = strtoll(aArgv[1], NULL, 10);

    Elapsed sDelete;

    switch (sChoice)
    {
        case 1:
            {
                printf("std::unique_ptr<char[]> buf(std::make_unique<char[]>(%zu));\n", sSize);

                Elapsed sElapsed;
                std::unique_ptr<char[]> buf(std::make_unique<char[]>(sSize));
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf.get(), sSize);
                sDelete.start();
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;
        case 2:
            {
                printf("std::unique_ptr<char[]> buf(new char[%zu]);\n", sSize);

                Elapsed sElapsed;
                std::unique_ptr<char[]> buf(new char[sSize]);
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf.get(), sSize);
                sDelete.start();
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;
        case 3:
            {
                printf("std::unique_ptr<char[]> buf((char *)malloc(%zu));\n", sSize);

                Elapsed sElapsed;
                std::unique_ptr<char[]> buf((char *)malloc(sSize));
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf.get(), sSize);
                sDelete.start();
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;
        case 4:
            {
                printf("char *buf = (char *)malloc(%zu);\n", sSize);

                Elapsed sElapsed;
                char *buf = (char *)malloc(sSize);
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf, sSize);
                sDelete.start();
                free(buf);
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;
        case 5:
            {
                printf("std::unique_ptr<char> buf(new char[%zu]);\n", sSize);

                Elapsed sElapsed;
                std::unique_ptr<char> buf(new char[sSize]);
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf.get(), sSize);
                sDelete.start();
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;
        case 6:
            {
                printf("std::unique_ptr<char[]> buf(new char[%zu]());\n", sSize);

                Elapsed sElapsed;
                std::unique_ptr<char[]> buf(new char[sSize]());
                printf("%ld usec to alloc buffer\n", sElapsed.getElapsedMicro());

                loop(buf.get(), sSize);
                sDelete.start();
            }
            printf("%ld usec to free buffer\n", sDelete.getElapsedMicro());
            break;

        default:
            printf("unknown method\n");
            break;

    }

    return 0;
}

结果:

仔细看看它花了多少次。 使用make_unique大约需要100毫秒,而new[]只需要42个用户。

test$ g++ -std=c++14 -Wall -Werror new.cpp -O3

test$ ./a.out 100000000 1
std::unique_ptr<char[]> buf(std::make_unique<char[]>(100000000));
64176 usec to alloc buffer
42887 usec to loop buffer
8683 usec to free buffer

test$ ./a.out 100000000 2
std::unique_ptr<char[]> buf(new char[100000000]);
41 usec to alloc buffer
90317 usec to loop buffer
8322 usec to free buffer

test$ ./a.out 100000000 3
std::unique_ptr<char[]> buf((char *)malloc(100000000));
38 usec to alloc buffer
89392 usec to loop buffer
8311 usec to free buffer

test$ ./a.out 100000000 4
char *buf = (char *)malloc(100000000);
44 usec to alloc buffer
89310 usec to loop buffer
8320 usec to free buffer

test$ ./a.out 100000000 5
std::unique_ptr<char> buf(new char[100000000]);
46 usec to alloc buffer
88960 usec to loop buffer
8315 usec to free buffer

test$ ./a.out 100000000 6
std::unique_ptr<char[]> buf(new char[100000000]());
65898 usec to alloc buffer
42891 usec to loop buffer
8689 usec to free buffer

test$ g++ --version
g++ (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

以下两个陈述之间的区别是什么?

std::unique_ptr<char[]> buf(std::make_unique<char[]>(size));
std::unique_ptr<char[]> buf(new char[size]);

valgrind给了我一些线索:make_unique()初始化内存而new没有:

test$ valgrind ./a.out 100 1
==21357== Memcheck, a memory error detector
==21357== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21357== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21357== Command: ./a.out 100 1
==21357==
std::unique_ptr<char[]> buf(std::make_unique<char[]>(100));
3880 usec to alloc buffer
555 usec to looping buffer
2927 usec to free buffer
==21357==
==21357== HEAP SUMMARY:
==21357==     in use at exit: 0 bytes in 0 blocks
==21357==   total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==21357==
==21357== All heap blocks were freed -- no leaks are possible
==21357==
==21357== For counts of detected and suppressed errors, rerun with: -v
==21357== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

test$ valgrind ./a.out 100 2
==21358== Memcheck, a memory error detector
==21358== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21358== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21358== Command: ./a.out 100 2
==21358==
std::unique_ptr<char[]> buf(new char[100]);
3224 usec to alloc buffer
==21358== Conditional jump or move depends on uninitialised value(s)
==21358==    at 0x4008E4: loop(char*, unsigned long) (in /home1/irteam/shawn/test/a.out)
==21358==    by 0x400AD9: main (in /home1/irteam/shawn/test/a.out)
==21358==
690 usec to looping buffer
2906 usec to free buffer
==21358==
==21358== HEAP SUMMARY:
==21358==     in use at exit: 0 bytes in 0 blocks
==21358==   total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==21358==
==21358== All heap blocks were freed -- no leaks are possible
==21358==
==21358== For counts of detected and suppressed errors, rerun with: -v
==21358== Use --track-origins=yes to see where uninitialised values come from
==21358== ERROR SUMMARY: 100 errors from 1 contexts (suppressed: 6 from 6)

test$ valgrind ./a.out 100 6
==7968== Memcheck, a memory error detector
==7968== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==7968== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==7968== Command: ./a.out 100 6
==7968==
std::unique_ptr<char[]> buf(new char[100]());
2509 usec to alloc buffer
2724 usec to looping buffer
788 usec to free buffer
==7968==
==7968== HEAP SUMMARY:
==7968==     in use at exit: 0 bytes in 0 blocks
==7968==   total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==7968==
==7968== All heap blocks were freed -- no leaks are possible
==7968==
==7968== For counts of detected and suppressed errors, rerun with: -v
==7968== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

如果差异实际上是否初始化内存,我可以在c ++规范中找到它吗?

BTW,我已经阅读了Differences between std::make_unique and std::unique_ptr

修改

  • 摆脱gettimeofday()
  • 将编译器优化级别提升到O3。

EDIT2

  • 添加了new运算符获取()并初始化缓冲区的第6种情况。

1 个答案:

答案 0 :(得分:1)

不是来自规范,但this std::make_unique reference表示数组创建重载等同于

unique_ptr<T>(new typename std::remove_extent<T>::type[size]())

分配基本等同于

new T[size]()

根据this new reference (construction section)表示每个元素都是value-initialized

对于像char这样的基本类型数组,这意味着每个元素都被初始化为零。

当你没有对数组进行值初始化时,数组元素将被默认构造,或者(对于基本类型)将不会被初始化。

没有初始化大型数组很快。初始化它,即使是全零,也不是那么快。哪个应该解释执行时间的差异。