코드를 작성할 때 컴파일 된 기계 코드를 고려해야합니까?


20

예를 들어 다음 코드가 있습니다.

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

그리고 나중에 이것을 여러 번 호출합니다. asm 코드에서 람다로 외부 호출을 보았습니다 .... 뭔가 ... 읽기가 쉽지 않고 성능을 유발할 수 있다고 생각합니다. 메타 프로그래밍에서 이길 수도 있지만 asm 디버깅 및 성능에서 잃을까요? 성능과 디버깅 단순성을 보장하기 위해 최신 언어 기능, 매크로 및 기타 메타 프로그래밍 측면을 피해야합니까?


1
컴파일러 버전과 번들 표준 라이브러리에 따라 람다는 실제로 비효율적으로 구현 될 수 있습니다. Stackoverflow 에서이 질문 을 참조하십시오 . 그러나 개선 책임은 컴파일러 공급 업체에 있습니다.
rwong

15
성능이 중요하지 않은 경우가 아니면 어셈블리 코드를 디버깅 할 필요가 없습니다. 또한 "깨끗한 코드"! = "좋은 성능".
BЈовић

들여 쓰기를 수정하십시오. 나는 그것을 시도했지만 공백 만 편집 할 수없는 것 같습니다.
Christoffer Hammarström

3
@Heather : Ratliff 스타일을 사용하고있는 것 같습니다. 이전에 본 적이 없으며 읽기가 어렵습니다. 확실히 덜 알려진 것들 중 하나입니다. 내 인상은 당신이 제대로 들여 쓰기되지 않은 것입니다. 당신이 그것을 읽을 수 있다고 생각한다면, 나는 단지 동의하지 않는다.
Christoffer Hammarström

1
if예제 코드에서 완전히 중복, 그리고 컴파일러는 아마 잡을 것 중에 잘못된 분기 예측을 유혹 할 이유가 없다.
dmckee

답변:


59

코드를 작성할 때 컴파일 된 기계 코드를 고려해야합니까?

아니요 , 코드를 처음 작성할 때 실제 측정 가능한 성능 문제가 발생하지 않습니다. 대부분의 작업에서 이것은 표준 사례입니다. 최적화에 대해 너무 일찍 생각하는 것을 "조기 최적화"라고하며, D. Knuth가이를 "모든 악의 근원" 이라고 부르는 데에는 충분한 이유가 있습니다 .

그렇습니다 . 실제로 입증 가능한 성능 병목 현상을 측정하고 특정 람다 구문을 근본 원인으로 식별합니다. 이 경우 Joel Spolsky의 "누설 추상화 법칙" 을 기억 하고 asm 레벨에서 발생할 수있는 일에 대해 생각하는 것이 좋습니다. 그러나 람다 구문을 "최신이 아닌"언어 구문 (적어도 괜찮은 C ++ 컴파일러를 사용하는 경우)으로 대체 할 때 성능 향상이 얼마나 작은 지 놀라게 될 수 있습니다.


2
+1 평소의 Doc에 따라 간결하고 정확하며 쉽게 따라 할 수 있습니다.
지미 호파

동의하고 매우 명확한 답변.
cnd

8

람다와 functor 클래스 중에서 선택하는 것은 트레이드 오프입니다.

람다로부터 얻는 이득은 상용구의 양을 최소화하고 개념 관련 코드를 활용하는 함수 내에서 (즉시 또는 나중에) 개념적으로 작성된 코드를 인라인으로 작성할 수있게함으로써 대부분 문법적입니다.

성능 측면에서 이것은 단일 "메소드"를 포함하는 C ++ 구조체 또는 클래스 인 functor 클래스 보다 나쁘지 않습니다 . 실제로 컴파일러는 람다를 컴파일러가 생성 한 functor 클래스와 다르게 처리합니다.

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

코드 예제에서 성능 측면에서는 함수 호출과 다르지 않습니다. functor 클래스에는 상태가 없기 때문에 (빈 캡처 절이 있으므로) 할당, 생성자 또는 소멸이 필요하지 않기 때문입니다.

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

디스어셈블러를 사용하여 사소한 C ++ 코드를 디버깅하는 것은 항상 어려운 작업이었습니다. 이것은 람다를 사용하거나 사용하지 않고 적용됩니다. 이는 C ++ 컴파일러의 정교한 코드 최적화로 인해 재정렬, 인터리빙 및 데드 코드 제거가 발생했습니다.

이름 변환 측면은 다소 적합하지 않으며 람다에 대한 디버거 지원은 아직 초기 단계 입니다. 디버거 지원이 시간이 지남에 따라 향상되기를 바랍니다.

현재 람다 코드를 디버깅하는 가장 좋은 방법은 소스 코드 수준에서 중단 점 설정을 지원하는 디버거를 사용하는 것입니다 (예 : 소스 파일 이름 및 줄 번호 지정).


3

@DocBrown의 답변에 추가하려면 요즘 CPU는 저렴하지만 인건비가 많이 듭니다.

프로그램의 전체 비용에서 하드웨어는 일반적으로 유지 보수 비용과 비교하여 사소한 수준입니다. 이는 유지 보수 비용과 비교할 때 일반적인 프로젝트에서 훨씬 더 비싼 부분입니다 (개발보다 더 큼).

따라서 코드는 성능이 중요한 경우를 제외하고 유지 관리를 고려해야하는 경우를 제외하고 다른 모든 것보다 유지 관리 를 최적화 해야합니다.


부분적으로 만 사실입니다. 코드에서 O (n ^ 2) (2 차)를 실행하고 O (log (n)) (대수)라고하는 것이 더 좋을 경우 하드웨어는 코드를 변경해도 성능이 크게 향상되지 않습니다. 원래 포스터에서 지정한 경우에는 매우 드 like니다.
gnash117

@ gnash117 — 예, 코드를 여러 번 실행해야한다면 옳습니다. 이것을 지적 해 주셔서 감사합니다. 이러한 경우 코드를 명확하게 문서화하면 코드를 유지 관리하면서 성능을 향상시킬 수 있습니다.
Paddy Landau

"노동은 비싸다"-맞다. 고객의 시간은 매우 중요하고 종종 비쌉니다.
Cerad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.