代码之家  ›  专栏  ›  技术社区  ›  Young-hwi

make\u unique与unique\u ptr(新T)之间的区别

  •  1
  • Young-hwi  · 技术社区  · 7 年前

    我认为以下两个是相同的:(使用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;
    }
    

    结果是:

    好好看看它用了多少usecs。 具有 make_unique 大约需要100毫秒 new[] 仅使用了42个USEC。

    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.
    

    以下两种说法之间的区别是什么?

    标准::unique\u ptr<字符[]>buf(标准::make\u unique<char[]>(size));
    标准::unique\u ptr<字符[]>buf(新字符[大小]);
    

    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++规范中我可以在哪里找到它?

    顺便说一句,我已经读过了 Differences between std::make_unique and std::unique_ptr .


    编辑

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

    编辑2

    • 添加了第6个案例 运算符获取 () 并初始化缓冲区。
    1 回复  |  直到 7 年前
        1
  •  1
  •   Some programmer dude    7 年前

    不符合规范,但 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 ,这意味着每个元素都初始化为零。

    如果不对数组进行值初始化,则将默认构造数组元素,或者(对于基元类型)根本不会初始化。

    不初始化大型阵列很快。初始化它,即使是全部为零,也没有那么快。这应该可以解释执行时间的差异。