"= default"대신 기본 생성자 "{}"를 사용하는 성능 차이가있는 이유는 무엇입니까?

엘리엇

최근에 다음과 같은 기본 생성자를 선언했기 때문에 성능이 저하되었음을 알았습니다.

Foo() = default;

대신에

Foo() {}

(참고로, 기본 생성자를 재정의하는 가변 생성자가 있었기 때문에 명시 적으로 선언해야했습니다)

이 두 줄의 코드가 동일하다고 생각했기 때문에 이상하게 보였습니다. 기본 생성자가 가능한 한 기본 생성자가 가능하지 않으면 두 번째 코드 줄에서 오류가 발생하고 첫 번째 줄은 오류가 발생합니다. 기본 생성자를 암시 적으로 삭제합니다. '내 상황이 아닙니다!).

좋아, 그래서 나는 작은 테스터를 만들었고 결과는 컴파일러에 따라 상당히 많이 다르지만 특정 설정을 사용하면 일관된 결과를 얻습니다.

#include <chrono>

template <typename T>
double TimeDefaultConstructor (int n_iterations)
{
    auto start_time = std::chrono::system_clock::now();

    for (int i = 0; i < n_iterations; ++i)
        T t;

    auto end_time = std::chrono::system_clock::now();

    std::chrono::duration<double> elapsed_seconds = end_time - start_time;

    return elapsed_seconds.count();
}

template <typename T, typename S>
double CompareDefaultConstructors (int n_comparisons, int n_iterations)
{
    int n_comparisons_with_T_faster = 0;

    for (int i = 0; i < n_comparisons; ++i)
    {
        double time_for_T = TimeDefaultConstructor<T>(n_iterations);
        double time_for_S = TimeDefaultConstructor<S>(n_iterations);

        if (time_for_T < time_for_S)    
            ++n_comparisons_with_T_faster;  
    }

    return (double) n_comparisons_with_T_faster / n_comparisons;
}


#include <vector>

template <typename T>
struct Foo
{
    std::vector<T> data_;

    Foo() = default;
};

template <typename T>
struct Bar
{
    std::vector<T> data_;

    Bar() {};
};

#include <iostream>

int main ()
{
    int n_comparisons = 10000;
    int n_iterations = 10000;

    typedef int T;

    double result = CompareDefaultConstructors<Foo<T>,Bar<T>> (n_comparisons, n_iterations);

    std::cout << "With " << n_comparisons << " comparisons of " << n_iterations
        << " iterations of the default constructor, Foo<" << typeid(T).name() << "> was faster than Bar<" << typeid(T).name() << "> "
        << result*100 << "% of the time" << std::endl;

    std::cout << "swapping orientation:" << std::endl;

    result = CompareDefaultConstructors<Bar<T>,Foo<T>> (n_comparisons, n_iterations);

    std::cout << "With " << n_comparisons << " comparisons of " << n_iterations
        << " iterations of the default constructor, Bar<" << typeid(T).name() << "> was faster than Foo<" << typeid(T).name() << "> "
        << result*100 << "% of the time" << std::endl;

    return 0;
}

위의 프로그램을 사용하면 다음 g++ -std=c++11과 유사한 출력이 일관되게 나타납니다.

기본 생성자의 10000 반복에 대한 10000 비교에서 Foo는 시간 스와핑 방향의 4.69 %보다 빠릅니다. 기본 생성자의 10000 반복에 대한 10000 비교에서 Bar는 Foo보다 96.23 % 더 빠릅니다.

컴파일러 설정을 변경하면 결과가 변경되는 것처럼 보이며 때로는 완전히 뒤집습니다. 그러나 내가 이해할 수없는 것은 그것이 왜 중요한가?

Evg

이 벤치 마크는 측정 대상을 측정하지 않습니다. 교체 Bar() {};Bar() = default;제작 Foo하고 Bar동일하고, 동일한 결과를 얻을 수 있습니다 :

기본 생성자의 10000 반복에 대한 10000 비교에서 Foo는 시간 스와핑 방향의 69.89 %보다 빠릅니다. 기본 생성자의 10000 반복에 대한 10000 비교에서는 Bar가 Foo보다 29.9 % 더 빠릅니다.

이것은 생성자가 아닌 다른 것을 측정하고 있다는 생생한 데모입니다.


-O1최적화 를 활성화 하면 for루프가 1T t;저하 됩니다 .

        test    ebx, ebx
        jle     .L3
        mov     eax, 0
.L4:
        add     eax, 1
        cmp     ebx, eax
        jne     .L4
.L3:

모두 FooBar. 즉, 사소한 for (int i = 0; i < n_iterations; ++i);루프로.

활성화 -O2하거나 -O3완전히 최적화되면.

최적화 ( -O0) 없이 다음 어셈블리를 얻습니다.

        mov     DWORD PTR [rbp-4], 0
.L35:
        mov     eax, DWORD PTR [rbp-4]
        cmp     eax, DWORD PTR [rbp-68]
        jge     .L34
        lea     rax, [rbp-64]
        mov     rdi, rax
        call    Foo<int>::Foo()
        lea     rax, [rbp-64]
        mov     rdi, rax
        call    Foo<int>::~Foo()
        add     DWORD PTR [rbp-4], 1
        jmp     .L35
.L34:

Bar으로 Foo대체 된 경우에도 동일합니다 Bar.

이제 생성자를 살펴 보겠습니다.

Foo<int>::Foo()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::vector()
        nop
        leave
        ret

Bar<int>::Bar()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::vector()
        nop
        leave
        ret

보시다시피 이것들도 동일합니다.


GCC 8.3 1 개

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

내 팩토리 메서드를 직접 사용하는 대신 기본 생성자가 필요한 이유는 무엇입니까?

분류에서Dev

본문이있는 생성자와 함께 "= default"를 사용하는 방법은 무엇입니까?

분류에서Dev

인라인 사용자 제공 생성자가 기본 클래스 생성자를 odr- 사용하는 이유는 무엇입니까?

분류에서Dev

Argparse가 생성하는 기본 도움말 메시지 대신 사용자 지정 메시지를 표시하는 방법은 무엇입니까?

분류에서Dev

Kotlin이 기본 생성자에서 코드를 허용하지 않는 이유는 무엇입니까?

분류에서Dev

삭제 된 기본 생성자가이를 포착하지 못하는 이유는 무엇입니까?

분류에서Dev

기본 복사 생성자에서 딥 복사를 구현하지 않는 이유는 무엇입니까?

분류에서Dev

C #에서 기본적으로 부모 생성자를 사용할 수없는 이유는 무엇입니까?

분류에서Dev

내 HTML 생성자 객체가 [HTMLElementElement] 대신 [object Object]를 반환하는 이유는 무엇입니까?

분류에서Dev

이 랜덤 생성기가 때때로 4 대신 3 자리를 반환하는 이유는 무엇입니까?

분류에서Dev

구조체에 사용할 수있는 기본 생성자가없는 이유는 무엇입니까?

분류에서Dev

JavaScript 생성자에 기능을 추가하지 않고 대신 프로토 타입을 통해 추가해야하는 이유는 무엇입니까?

분류에서Dev

TypeScript가 기호 속성에 대해 유형 축소를 사용하지 않는 이유는 무엇입니까?

분류에서Dev

신호 처리기를 사용하여 스스로를 죽이는 스크립트가 분할 오류를 생성하는 이유는 무엇입니까?

분류에서Dev

사소한 기본 생성자가 아무 작업도 수행하지 않는 경우 malloc을 사용하여 사소하게 구성 가능한 객체를 만들 수없는 이유는 무엇입니까?

분류에서Dev

gcc가 std :: vector <> 복사를 위해 memcpy 대신 memmove를 생성하는 이유는 무엇입니까?

분류에서Dev

누군가 기본 이동 생성자를 생성하지만 이동 할당 연산자를 삭제해야하는 이유는 무엇입니까?

분류에서Dev

많은 Linux 배포판이 기능 대신 setuid를 사용하는 이유는 무엇입니까?

분류에서Dev

이동 생성자를 사용하는 이유는 무엇입니까?

분류에서Dev

대기를 차단하는 대신 비 차단 waitpid를 사용하는 이유는 무엇입니까?

분류에서Dev

C ++에서 부모 기본 생성자를 암시 적으로 호출하는 이유는 무엇입니까?

분류에서Dev

내 FormLoad 이벤트 핸들러가 인수가있는 생성자가 아닌 사용자 정의 클래스의 기본 생성자를 호출하는 이유는 무엇입니까?

분류에서Dev

LSTM Autoencoder가 활성화 기능으로 'relu'를 사용하는 이유는 무엇입니까?

분류에서Dev

C # 대 C ++ 성능-.NET이 가장 기본적인 최적화 (예 : 데드 코드 제거)를 수행하지 않는 이유는 무엇입니까?

분류에서Dev

참조를 사용하는 대신 수정하기 위해 속성을 명시 적으로 참조해야하는 이유는 무엇입니까?

분류에서Dev

gdb를 사용하여 기본 클래스 생성자를 중단하는 방법은 무엇입니까?

분류에서Dev

생성자 대신 별도의로드 방법을 사용하는 이유는 무엇입니까?

분류에서Dev

-P 대신 -D를 사용하여 Maven 프로필을 활성화하는 이유는 무엇입니까?

분류에서Dev

기본 파일 IO에서 문자열 대신 숫자를 가져 오는 이유는 무엇입니까?

Related 관련 기사

  1. 1

    내 팩토리 메서드를 직접 사용하는 대신 기본 생성자가 필요한 이유는 무엇입니까?

  2. 2

    본문이있는 생성자와 함께 "= default"를 사용하는 방법은 무엇입니까?

  3. 3

    인라인 사용자 제공 생성자가 기본 클래스 생성자를 odr- 사용하는 이유는 무엇입니까?

  4. 4

    Argparse가 생성하는 기본 도움말 메시지 대신 사용자 지정 메시지를 표시하는 방법은 무엇입니까?

  5. 5

    Kotlin이 기본 생성자에서 코드를 허용하지 않는 이유는 무엇입니까?

  6. 6

    삭제 된 기본 생성자가이를 포착하지 못하는 이유는 무엇입니까?

  7. 7

    기본 복사 생성자에서 딥 복사를 구현하지 않는 이유는 무엇입니까?

  8. 8

    C #에서 기본적으로 부모 생성자를 사용할 수없는 이유는 무엇입니까?

  9. 9

    내 HTML 생성자 객체가 [HTMLElementElement] 대신 [object Object]를 반환하는 이유는 무엇입니까?

  10. 10

    이 랜덤 생성기가 때때로 4 대신 3 자리를 반환하는 이유는 무엇입니까?

  11. 11

    구조체에 사용할 수있는 기본 생성자가없는 이유는 무엇입니까?

  12. 12

    JavaScript 생성자에 기능을 추가하지 않고 대신 프로토 타입을 통해 추가해야하는 이유는 무엇입니까?

  13. 13

    TypeScript가 기호 속성에 대해 유형 축소를 사용하지 않는 이유는 무엇입니까?

  14. 14

    신호 처리기를 사용하여 스스로를 죽이는 스크립트가 분할 오류를 생성하는 이유는 무엇입니까?

  15. 15

    사소한 기본 생성자가 아무 작업도 수행하지 않는 경우 malloc을 사용하여 사소하게 구성 가능한 객체를 만들 수없는 이유는 무엇입니까?

  16. 16

    gcc가 std :: vector <> 복사를 위해 memcpy 대신 memmove를 생성하는 이유는 무엇입니까?

  17. 17

    누군가 기본 이동 생성자를 생성하지만 이동 할당 연산자를 삭제해야하는 이유는 무엇입니까?

  18. 18

    많은 Linux 배포판이 기능 대신 setuid를 사용하는 이유는 무엇입니까?

  19. 19

    이동 생성자를 사용하는 이유는 무엇입니까?

  20. 20

    대기를 차단하는 대신 비 차단 waitpid를 사용하는 이유는 무엇입니까?

  21. 21

    C ++에서 부모 기본 생성자를 암시 적으로 호출하는 이유는 무엇입니까?

  22. 22

    내 FormLoad 이벤트 핸들러가 인수가있는 생성자가 아닌 사용자 정의 클래스의 기본 생성자를 호출하는 이유는 무엇입니까?

  23. 23

    LSTM Autoencoder가 활성화 기능으로 'relu'를 사용하는 이유는 무엇입니까?

  24. 24

    C # 대 C ++ 성능-.NET이 가장 기본적인 최적화 (예 : 데드 코드 제거)를 수행하지 않는 이유는 무엇입니까?

  25. 25

    참조를 사용하는 대신 수정하기 위해 속성을 명시 적으로 참조해야하는 이유는 무엇입니까?

  26. 26

    gdb를 사용하여 기본 클래스 생성자를 중단하는 방법은 무엇입니까?

  27. 27

    생성자 대신 별도의로드 방법을 사용하는 이유는 무엇입니까?

  28. 28

    -P 대신 -D를 사용하여 Maven 프로필을 활성화하는 이유는 무엇입니까?

  29. 29

    기본 파일 IO에서 문자열 대신 숫자를 가져 오는 이유는 무엇입니까?

뜨겁다태그

보관