developer tip

std :: function의 성능 오버 헤드는 무엇입니까?

optionbox 2020. 12. 8. 07:58
반응형

std :: function의 성능 오버 헤드는 무엇입니까?


std::function<>성능 저하 원인을 사용하는 포럼에서 들었습니다 . 사실인가요? 사실이라면 큰 성능 저하입니까?


boost의 참조 자료에서 정보를 찾을 수 있습니다. boost :: function을 통한 호출은 얼마나 많은 오버 헤드를 발생합니까? 성능

이것은 기능 향상을위한 "예 또는 아니오"를 결정하지 않습니다. 프로그램의 요구 사항에 따라 성능 저하가 허용 될 수 있습니다. 대개 프로그램의 일부는 성능에 중요하지 않습니다. 그리고 그래도 받아 들일 수 있습니다. 이것은 당신이 결정할 수있는 것입니다.

표준 라이브러리 버전과 관련하여 표준은 인터페이스 만 정의합니다. 작동하도록하는 것은 전적으로 개별 구현에 달려 있습니다. boost의 기능과 유사한 구현이 사용될 것이라고 생각합니다.


실제로 std:function사용할 때마다 고려해야 할 성능 문제 가 있습니다. 의 주요 강점 std::function, 즉 유형 삭제 메커니즘은 무료로 제공되지 않으며 이에 대한 대가를 지불해야 할 수도 있습니다 (반드시 필수는 아님).

std::function호출 가능한 유형을 래핑하는 템플릿 클래스입니다. 그러나 호출 가능한 유형 자체에 대해서는 매개 변수화되지 않고 반환 및 인수 유형에만 매개 변수화됩니다. 호출 가능 유형은 생성시에만 알려 지므로 std::function생성자에 제공된 객체의 복사본을 보유하기 위해이 유형의 미리 선언 된 멤버를 가질 수 없습니다.

대략적으로 말하면 (실제로는 그보다 더 복잡합니다) std::function생성자에게 전달 된 객체에 대한 포인터 만 보유 할 수 있으며 이는 수명 문제를 발생시킵니다. 포인터가 개체의 수명보다 수명이 짧은 개체를 가리키면 std::function내부 포인터가 매달려 있습니다. 이 문제를 방지하기 위해 std::function호출 operator new(또는 사용자 지정 할당 자) 을 통해 힙에있는 개체의 복사본을 만들 수 있습니다 . 동적 메모리 할당은 사람들이에서 암시하는 성능 패널티로 가장 많이 언급하는 것 std::function입니다.

나는 최근에 더 자세한 내용을 담은 기사를 썼고, 메모리 할당 비용을 지불하는 것을 피할 수있는 방법과 장소를 설명했습니다.

http://drdobbs.com/cpp/232500059


인수를 바인딩하지 않고 함수를 전달하는 경우 (힙 공간을 할당하지 않음) 여부에 따라 크게 달라집니다.

또한 다른 요인에 따라 다르지만 이것이 주요 요인입니다.

비교할 무언가가 필요하다는 것은 사실이며, 전혀 사용하지 않는 것에 비해 단순히 '오버 헤드를 줄인다'고 말할 수 없으며, 함수를 전달하는 다른 방법을 사용하는 것과 비교해야합니다. 그리고 전혀 사용하지 않아도된다면 처음부터 필요하지 않았을 것입니다.


첫째, 함수 내부에서 오버 헤드가 작아집니다. 워크로드가 높을수록 오버 헤드가 줄어 듭니다.

둘째 : g ++ 4.5는 가상 기능과 비교하여 차이가 없습니다.

main.cc

#include <functional>
#include <iostream>

// Interface for virtual function test.
struct Virtual {
    virtual ~Virtual() {}
    virtual int operator() () const = 0;
};

// Factory functions to steal g++ the insight and prevent some optimizations.
Virtual *create_virt();
std::function<int ()> create_fun();
std::function<int ()> create_fun_with_state();

// The test. Generates actual output to prevent some optimizations.
template <typename T>
int test (T const& fun) {
    int ret = 0;
    for (int i=0; i<1024*1024*1024; ++i) {
        ret += fun();
    }    
    return ret;
}

// Executing the tests and outputting their values to prevent some optimizations.
int main () {
    {
        const clock_t start = clock();
        std::cout << test(*create_virt()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "virtual: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun_with_state()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function with bindings: " << secs << " secs.\n";
    }
}

impl.cc

#include <functional>

struct Virtual {
    virtual ~Virtual() {}
    virtual int  operator() () const = 0;
};
struct Impl : Virtual {
    virtual ~Impl() {}
    virtual int  operator() () const { return 1; }
};

Virtual *create_virt() { return new Impl; }

std::function<int ()> create_fun() { 
    return  []() { return 1; };
}

std::function<int ()> create_fun_with_state() { 
    int x,y,z;
    return  [=]() { return 1; };
}

출력 g++ --std=c++0x -O3 impl.cc main.cc && ./a.out:

1073741824
virtual: 2.9 secs.
1073741824
std::function: 2.9 secs.
1073741824
std::function with bindings: 2.9 secs.

So, fear not. If your design/maintainability can improve from prefering std::function over virtual calls, try them. Personally, I really like the idea of not forcing interfaces and inheritance on clients of my classes.

참고URL : https://stackoverflow.com/questions/5057382/what-is-the-performance-overhead-of-stdfunction

반응형