최적화 된 코드를 읽을 수있는 코드로 교체해도 괜찮습니까?


78

때로는 기존 코드를 확장 / 개선 해야하는 상황이 발생할 수 있습니다. 이전 코드는 매우 간결하지만 확장하기가 어렵고 읽는 데 시간이 걸립니다.

현대 코드로 바꾸는 것이 좋은 생각입니까?

얼마 전 저는 린 접근법을 좋아했지만 이제는 더 높은 추상화, 더 나은 인터페이스 및 더 읽기 쉽고 확장 가능한 코드를 선호하여 많은 최적화를 희생하는 것이 좋습니다.

컴파일러도 나아지고있는 것 같습니다. 따라서 struct abc = {}자동으로로 바뀌고 memset, shared_ptr원시 포인터 twiddling과 거의 동일한 코드를 생성하고, 템플릿은 매우 희박한 코드를 생성하기 때문에 매우 잘 작동합니다.

그러나 여전히 스택 기반 배열과 오래된 C 함수가 불분명 한 논리로 표시되는 경우가 많으며 일반적으로 중요한 경로에 있지 않습니다.

작은 코드를 만져야한다면 그러한 코드를 바꾸는 것이 좋은 생각입니까?


20
가독성과 최적화는 대부분 반대되지 않습니다.
deadalnix

23
일부 의견으로 가독성을 향상시킬 수 있습니까?
YetAnotherUser

17
OOP 인증이 '현대 코드'로 간주 될까 걱정됩니다
James

7
슬랙웨어 철학처럼 : 그것이 깨지지 않았다면 그것을 고치지 마라. 최소한 덜할만한 이유가있다.
osdamv

5
최적화 된 코드 란 실제 최적화 된 코드 또는 소위 최적화 된 코드를 의미합니까?
dan04

답변:


115

어디?

  • Google 웹 사이트의 홈페이지에는 허용되지 않습니다. 가능한 빨리 물건을 보관하십시오.

  • 1 년에 한 사람이 사용하는 응용 프로그램의 일부에서 코드 가독성을 얻기 위해 성능을 희생하는 것은 완벽하게 허용됩니다.

일반적으로 작업중 인 코드 부분의 비 기능적 요구 사항은 무엇입니까? 동작이 900ms 미만으로 수행되어야하는 경우 주어진 상황 (기계, 부하 등)에서 80 %의 시간이며 실제로는 200 ms 미만에서 수행됩니다. 시간의 100 %는 성능에 약간의 영향을 줄지라도 코드를 더 읽기 쉽게 만듭니다. 반면에 동일한 조치가 10 초 이내에 수행되지 않은 경우 성능에 어떤 문제가 있는지 (또는 우선 요구 사항) 확인해야합니다.

또한 가독성 향상으로 성능이 어떻게 저하됩니까? 종종 개발자는 조기 최적화에 가까운 동작을 조정합니다. 가독성을 높이고 성능을 크게 떨어 뜨릴 것이라고 믿지만 읽기 쉬운 코드는 동일한 작업을 수행하는 데 몇 마이크로 초가 더 걸립니다.


47
+1! 숫자가 없으면 숫자를 얻으십시오. 숫자를 얻을 시간이 없다면 변경할 시간이 없습니다.
Tacroy

49
예를 들어, 개발자는 "C"가 "C ++"보다 빠르다고 가정하고, 백업 할 숫자가없는 것이 빠르다는 일반적인 느낌에서 C ++ 기능을 피함으로써 신화와 오해에 따라 "최적화"하는 경우가 종종 있습니다. gotofor 루프보다 빠르다고 생각한 C 개발자를 생각 나게합니다 . 반어로, 옵티마이 저는 더 나은 for 루프로했다, 그래서 그는 코드가 모두 느린했다 읽기 어렵습니다.
Steven Burnap

6
다른 답변을 추가하는 대신이 답변에 +1했습니다. 코드 조각을 이해하는 것이 중요하다면 주석을 잘 달아주십시오. 저는 수십 명의 기고자와 함께 10 년 된 레거시 코드로 C / C ++ / 어셈블리 환경에서 일했습니다. 코드가 작동하면 그대로두고 작업을 다시 시작하십시오.
Chris K

그렇기 때문에 읽을 수있는 코드 만 작성하는 경향이 있습니다. 몇 개의 핫스팟을 절단하여 성능에 도달 할 수 있습니다.
Luca

36

일반적으로 no 입니다.

코드를 변경하면 시스템의 다른 곳에서 예기치 않은 노크 문제가 발생할 수 있습니다 (단, 단위 및 연기 테스트가 제대로 이루어지지 않은 경우 프로젝트에서 나중에 나중에 눈에 띄지 않을 수 있음). 나는 보통 "파산하지 않으면 고치지 말라"는 생각에 의해 움직입니다.

이 규칙의 예외는이 코드를 다루는 새로운 기능을 구현하는 경우입니다. 이 시점에서 의미가없고 리팩토링이 실제로 수행되어야하는 경우 리팩토링 시간 (및 노크 문제를 처리하기위한 충분한 테스트 및 버퍼)이 모두 추정에 포함되는 한 계속 진행하십시오.

물론 profile, profile, profile , 특히 중요한 경로 영역 인 경우.


2
예, 그러나 최적화가 필요하다고 가정합니다. 우리는 그것이 언제인지 항상 알지 못하며, 아마도 이것을 먼저 결정하고 싶을 것입니다.
haylem

2
@ haylem : 아니요, 코드 그대로 작동 한다고 가정합니다 . 또한 코드를 리팩토링하면 시스템의 다른 곳에서 항상 문제가 발생한다고 가정합니다 (외부 종속성이없는 사소한 코드 덩어리를 처리하지 않는 한).
데미안 브레히트

이 답변에는 약간의 진실이 있으며, 아이러니하게도 이는 개발자가 녹아웃 문제를 거의 문서화, 이해, 전달 또는주의를 기울이지 않기 때문입니다. 개발자가 과거에 발생한 문제에 대해 더 깊이 이해하고 있다면 무엇을 측정해야하는지 알 수 있으며 코드 변경에 더 자신감이 생길 것입니다.
rwong

29

요약 : 그것은 달려있다

  • 리팩토링 / 향상된 버전이 실제로 필요합니까?

    • 즉각적 또는 장기적인 구체적인 이익이 있습니까?
    • 이것이 유지 보수성, 또는 실제로 건축적인 것만을위한 이득입니까?
  • 실제로 최적화해야합니까?

    • 왜?
    • 어떤 목표 이득을 목표로해야합니까?

세부 사항에서

깨끗하고 반짝이는 물건이 필요합니까?

여기에는주의해야 할 것이 있으며, 실제, 측정 가능한 이득과 개인 취향 및 코드를 만질 수없는 나쁜 나쁜 습관 사이의 한계를 식별해야합니다.

더 구체적으로, 이것을 아십시오 :

Over-Engineering 과 같은 것이 있습니다

안티 패턴이며 기본 제공 문제가 있습니다.

  • 그것은 더 확장 될 수있다 ,하지만 쉽게되지 않을 수도 있습니다 , 연장
  • 이해하기 간단하지 않을 수 있습니다 ,
  • 마지막으로, 적어도 여기에는 : 전체 코드 속도가 느려질 수 있습니다.

일부는 KISS 원칙 을 참조로 언급 할 수도 있지만, 여기에는 반 직관적 인 방법이 있습니다. 최적화 된 방법은 단순하거나 깔끔한 아키텍처 방식입니까? 아래의 나머지 부분에서 설명한 것처럼 답이 반드시 절대적인 것은 아닙니다.

당신은 그것을 필요로하지 않습니다

YAGNI의 원리는 다른 문제와 완전히 직교하지 않지만 자신에게 질문을하는 데 도움이 : 당신이 그것을 필요로하는 건가요?

보다 복잡한 아키텍처는 유지 관리가 용이 ​​한 것처럼 보이는 것 외에도 실제로 이점이 있습니까?

고장 나지 않았다면 고치지 마십시오

이 포스터를 큰 포스터에 적어 화면 옆이나 직장의 주방 공간 또는 개발자 회의실에 매달아 놓으십시오. 물론 자신을 되풀이 할 가치가있는 다른 많은 진언이 있지만,이 특정한 것은 "유지 보수 작업"을 수행하고 "개선"하려는 충동을 느낄 때마다 중요합니다.

이해하기 위해 코드를 읽을 때 코드를 "개선"하거나 무의식적으로 만지는 것이 자연 스럽습니다. 그것은 우리가 의견을 가지고 내부에 대해 더 깊이 이해하려고 노력한다는 것을 의미하기 때문에 좋은 일이지만 기술 수준과 지식에 묶여 있습니다 (더 나은 것을 결정하는 방법은 무엇입니까? ...), 우리가 소프트웨어에 대해 알고 있다고 생각하는 모든 가정 ::

  • 실제로,
  • 실제로해야합니다.
  • 결국해야 할 것입니다
  • 그리고 얼마나 잘하는지.

실제로 최적화해야합니까?

이 모든 것이 처음에 왜 "최적화"되었습니까? 그들은 조기 최적화 가 모든 악의 근원 이라고 말하며 , 문서화되지 않은 것처럼 보이고 최적화 된 코드를 보게되면 일반적으로 최적화 규칙을 따르지 않았고 최적화 노력이 필요하지 않은 것으로 가정 할 수 있습니다. 평범한 개발자의 허비가 시작됩니다. 또 다시, 아마도 당신의 이야기 일 것입니다.

그렇다면 어느 한도 내에서 수용 할 수 있습니까? 필요하다면,이 한계가 존재하며, 개선 할 여지가 생길 수 있습니다.

또한 보이지 않는 특성에주의하십시오. 이 코드의 "확장 가능"버전은 런타임시 더 많은 메모리를 사용하고 실행 파일에 대해 더 큰 정적 메모리 공간을 제공 할 가능성이 있습니다. Shiny OO 기능에는 이와 같은 직관적이지 않은 비용이 포함되어 있으며 프로그램과 실행 환경에 중요 할 수 있습니다.

측정, 측정, 측정

구글 사람들은 이제 데이터에 관한 것입니다! 데이터로 백업 할 수 있다면 필요합니다.

개발에 소비 된 1 달러당 적어도 1 달러의 테스트와 1 달러 이상의 지원 이 뒤따를 것이라는 오래된 이야기는 없습니다 (그러나 실제로는 훨씬 더 많습니다).

변화는 많은 것들에 영향을 미칩니다.

  • 새로운 빌드를 만들어야 할 수도 있습니다.
  • 새로운 단위 테스트를 작성해야합니다 (아무것도없는 경우에는 더 확장 가능하며 더 확장 가능한 아키텍처는 버그에 대한 표면이 더 많기 때문에 더 많은 공간을 남겨 둘 수 있습니다).
  • 새로운 성능 테스트를 작성해야합니다 (향후에 안정적으로 유지되고 병목 현상이 발생하는 위치를 확인 하기 위해) .
  • 이를 문서화해야합니다 (확장 성이 높을수록 세부 사항을위한 더 많은 공간이 필요함).
  • 귀하 (또는 다른 사람)는 품질 관리에서이를 광범위하게 다시 테스트해야합니다.
  • 코드는 (거의) 버그가 없으며 결코 지원해야합니다.

따라서 여기서 측정해야 할 것은 하드웨어 리소스 소비 (실행 속도 또는 메모리 풋 프린트)뿐만 아니라 팀 리소스 소비 이기도 합니다 . 둘 다 개발 목표에 따라 목표 목표를 정의하고, 측정하고, 설명하고, 조정해야합니다.

그리고 관리자에게는 이것이 현재 개발 계획에 적합하다는 것을 의미하므로 이에 대해 의사 소통하고 분노한 카우보이 / 잠수함 / 블랙-옵스 코딩에 참여하지 마십시오.


일반적으로 ...

네,하지만...

내가 틀리지 말아라. 일반적으로, 나는 당신이 제안한 이유에 찬성 할 것이다. 그리고 나는 종종 그것을 옹호한다. 그러나 장기 비용을 알고 있어야합니다.

완벽한 세상에서 올바른 솔루션입니다.

  • 컴퓨터 하드웨어 는 시간이 지남에 따라 더 좋아지고
  • 컴파일러와 런타임 플랫폼은 시간이 지남에 따라 향상됩니다.
  • 완벽에 가깝고 깨끗하며 유지 관리 가능하며 읽을 수있는 코드를 얻습니다.

실제로:

  • 당신은 그것을 악화시킬 수 있습니다

    더 많은 안구가 필요하며, 더 복잡하게할수록 더 많은 안구가 필요합니다.

  • 당신은 미래를 예측할 수 없습니다

    필요한 "확장"이 이전 형태로 구현하기가 더 쉽고 빠르지 않았고, 자체 최적화가 필요한 경우에도 절대 확실성을 알 수는 없습니다. .

  • 그것은 경영진의 관점에서 직접적인 이득이없는 막대한 비용을 나타냅니다.

프로세스의 일부로 만들기

여기서는 약간의 변화이며 특정 문제를 염두에 두었습니다. 이 경우에는 일반적으로 괜찮다고 말하지만, 대부분의 사람들에게는 작은 변화, 거의 외과 적 타격 편집에 대한 개인적인 이야기가 있습니다. 코드 뒤에있는 이유 중 하나가되어서는 안되는 것을 만졌습니다.

그러한 결정을 처리 할 수있는 프로세스가 있다면 다음과 같은 개인적인 결정을 내릴 수 있습니다.

  • 올바르게 테스트하면 문제가 발생하면 더 빨리 알게됩니다.
  • 측정하면 개선되었는지 알 수 있습니다.
  • 당신이 그것을 검토하면 사람들이 떨어져 있는지 알 수 있습니다.

테스트 범위, 프로파일 링 및 데이터 수집이 까다 롭습니다

그러나 테스트 코드와 메트릭은 실제 코드에서 피하려고하는 것과 동일한 문제로 인해 어려움을 겪을 수 있습니다. 올바른 것을 테스트하고 미래에 올바른 것입니까? 소지품?

그럼에도 불구하고 일반적으로 더 많은 테스트 (특정 한도까지) 및 측정이 많을수록 더 많은 데이터를 수집하고 더 안전합니다. 나쁜 유추 시간 : 운전 (또는 일반적인 삶)과 같이 생각하십시오 : 자동차가 당신을 고장 났거나 누군가가 오늘 자신의 차를 운전하여 자살하기로 결정했다면, 당신은 세계에서 가장 좋은 운전자가 될 수 있습니다. 기술이 충분하지 않을 수 있습니다. 당신을 때릴 수있는 환경적인 것들이 있으며 인적 오류도 중요합니다.

코드 검토는 개발 팀의 복도 테스트입니다

그리고 마지막 부분이 핵심이라고 생각합니다. 코드 검토를하십시오. 개선 사항을 솔로로 만들면 개선의 가치를 알 수 없습니다. 코드 검토는 "홀 웨이 테스트"입니다. 버그를 탐지하고 과도 공학 및 기타 안티 패턴을 탐지하고 팀의 능력과 일치하는지 확인 하려면 Raymond의 Linus 's Law 버전을 따르십시오 . 다른 사람이 없다면 "최상의"코드를 가질 필요는 없습니다. 그러나이를 이해하고 유지할 수 있으며, 이는 암호화 최적화와 6 계층 심층 아키텍처 설계 모두에 적용됩니다.

마지막 단어로 다음을 기억하십시오.

디버깅은 프로그램을 처음 작성하는 것보다 두 배나 어렵다는 것을 누구나 알고 있습니다. 당신이 그것을 쓸 때 당신이 할 수있는만큼 영리하다면 어떻게 그것을 디버깅 할 것인가? - 브라이언 커니 건


"파산하지 않으면 해결하지 마십시오"는 리팩토링에 반대합니다. 어떤 것이 작동하는지, 유지 불가능한 경우 변경해야하는 것은 중요하지 않습니다.
미야모토 아키라

@MiyamotoAkira : 두 가지 속도입니다. 그것이 깨지지 않았지만 수용 가능하고 지원을 받을 가능성 이 적 다면, 잠재적 인 새로운 버그를 도입하거나 개발 시간을 소비하는 대신 혼자 남겨 두는 것이 허용 될 수 있습니다. 리팩토링의 장단기 이점을 평가하는 것이 전부입니다. 명확한 답은 없으며 평가가 필요합니다.
haylem

동의했다. 나는 리팩토링을 기본 옵션으로보고 있기 때문에 문장 (및 그 철학)을 좋아하지 않는다고 가정하고 너무 오래 걸리거나 너무 어려워 보일 경우에만 결정하지 않아야합니다. 그것으로 가십시오. 나는 당신이 일을하더라도 그것을 유지하거나 연장 해야하는 즉시 분명히 잘못된 해결책 이었다는 것을 바꾸지 않는 사람들에 의해 화상을 입었습니다.
미야모토 아키라

@MiyamotoAkira : 짧은 문장과 의견 진술은 많이 표현할 수 없습니다. 그것들은 당신의 얼굴에 있고 측면에서 개발되어야합니다. 큰 안전망이 없거나 많은 이유 없이도 가능한 한 자주 코드를 검토하고 코드를 작성하는 요소가 매우 중요합니다. 더러워지면 청소하십시오. 그러나 마찬가지로, 나는 또한 몇 번 화상을 입었다. 그리고 여전히 화상을 입을 것입니다. 그것이 3 도가 아닌 한, 나는별로 신경 쓰지 않습니다. 지금까지는 항상 장기적인 이익을 위해 단기 화상이었습니다.
haylem

8

일반적으로 가독성과 우선 성능에 중점을 두어야합니다. 대부분의 경우 이러한 성능 최적화는 무시할 만하지 만 유지 관리 비용은 엄청날 수 있습니다.

분명히 모든 "작은"것은 명확성을 위해 변경되어야합니다. 왜냐하면 당신이 지적했듯이 대부분은 컴파일러에 의해 최적화 될 것입니다.

더 큰 최적화의 경우 최적화가 실제로 합리적인 성능에 도달하는 데 중요 할 수 있습니다 (놀랍게도 그렇지는 않지만). 변경을 한 다음 변경 전후에 코드를 프로파일 링합니다. 새 코드에 중대한 성능 문제가있는 경우 항상 최적화 된 버전으로 롤백 할 수 있으며, 그렇지 않은 경우 더 깨끗한 코드 버전을 사용할 수 있습니다.

한 번에 코드의 한 부분 만 변경하고 각 리팩토링 후 성능에 어떤 영향을 미치는지 확인하십시오.


8

코드가 최적화 된 이유와 코드 변경의 영향 및 코드가 전체 성능에 미치는 영향에 따라 다릅니다. 또한 테스트 변경 사항을로드하는 좋은 방법이 있는지 여부에 따라 달라집니다.

프로덕션에서 볼 수있는 것과 유사한로드 전후에 프로파일 링없이 이러한 변경을 수행해서는 안됩니다. 즉, 한 명의 사용자 만 시스템을 사용하는 경우 개발자 컴퓨터에서 작은 데이터 하위 집합을 사용하거나 테스트하지 않습니다.

최적화가 최신 인 경우 개발자에게 문의하여 문제가 무엇인지, 최적화 전에 애플리케이션이 얼마나 느 렸는지 정확히 알 수 있습니다. 이를 통해 최적화를 수행 할 가치가 있는지 여부와 최적화에 필요한 조건에 대해 많은 것을 알 수 있습니다 (예를 들어 변경 사항을 테스트하는 경우 9 월 또는 10 월까지 1 년 내내 보고서가 느려지지 않을 수 있음) 2 월에는 속도가 아직 명확하지 않고 테스트가 유효하지 않을 수 있습니다.

최적화가 다소 오래된 경우 새로운 방법이 더 빠를뿐만 아니라 더 읽기 쉽습니다.

궁극적으로 이것은 상사에게 질문입니다. 최적화 된 항목을 리팩토링하고 변경이 최종 결과에 영향을 미치지 않으며 이전 방식과 비교하여 성능이 좋거나 최소한 수용 가능한지 확인하려면 시간이 많이 걸립니다. 그는 몇 분의 코딩 시간을 절약하기 위해 고위험 작업을 수행하는 대신 다른 영역에서 시간을 보내길 원할 수 있습니다. 또는 코드를 이해하기 어렵고 자주 개입해야하며 더 나은 방법을 사용할 수 있다는 데 동의 할 수도 있습니다.


6

경우 프로파일이 최적화는 (그것이 중요 섹션에없는) 필요로하지 않는 것을 보여줍니다 또는 (나쁜 조기 최적화의 결과로) 나쁜 런타임을 가지고 다음 있는지 쉽게 유지 관리 할 수있는 읽을 수있는 코드로 교체

또한 적절한 테스트에서 코드가 동일하게 작동하는지 확인하십시오


5

비즈니스 관점에서 생각하십시오. 변경 비용은 얼마입니까? 코드를보다 쉽게 ​​확장 또는 유지 관리 할 수있게함으로써 얼마나 많은 시간을 변경하고 장기적으로 절약 할 수 있습니까? 이제 가격표를 해당 시간에 첨부하고 성능 저하로 손실 된 돈과 비교하십시오. 손실 된 성능을 보충하기 위해 서버를 추가하거나 업그레이드해야 할 수도 있습니다. 제품이 더 이상 요구 사항을 충족하지 않아 더 이상 판매 할 수 없습니다. 아마도 손실이 없을 것입니다. 어쩌면 변화가 견고성을 높이고 다른 곳에서 시간을 절약 할 수 있습니다. 이제 결정하십시오.

참고로, 경우에 따라 조각의 두 버전을 모두 유지하는 것이 가능할 수도 있습니다. 임의의 입력 값을 생성하는 테스트를 작성하고 다른 버전으로 결과를 확인할 수 있습니다. "영리한"솔루션을 사용하여 완벽하게 이해 가능하고 명백히 정확한 솔루션의 결과를 확인하여 새로운 솔루션이 이전 솔루션과 동등한 지 확인하십시오 (그러나 증거는 아님). 또는 다른 길로 가서 자세한 코드로 까다로운 코드의 결과를 확인하여 모호한 방식으로 해킹의 의도를 문서화하십시오.


4

기본적으로 리팩토링 이 가치있는 벤처 인지 묻는 것 입니다. 이에 대한 대답은 가장 그렇습니다.

그러나...

... 조심히해야합니다. 리팩토링하는 모든 코드에 대해 견고한 단위, 통합, 기능 및 성능 테스트가 필요합니다. 필요한 모든 기능을 실제로 테스트한다는 확신이 있어야합니다. 쉽고 반복적으로 실행할 수 있어야합니다. 그런 후에는 해당 기능을 포함하는 새 구성 요소로 구성 요소를 교체 할 수 있습니다.

마틴 파울러 가이 을 썼습니다 .


3

적절한 이유없이 작업 코드를 변경해서는 안됩니다. "리팩토링"은 리팩토링 없이는 작업을 수행 할 수없는 한 충분한 이유가 아닙니다 . 어려운 코드 자체 내에서 버그를 수정하는 것이라도 시간을내어 이해하고 가능한 작은 변경을해야합니다. 코드가 이해하기 어려운 경우 코드를 완전히 이해할 수 없으므로 변경 사항이 있으면 예기치 않은 부작용, 즉 버그가 발생할 수 있습니다. 변경이 클수록 문제가 발생할 가능성이 높습니다.

이해할 수없는 코드에 완전한 단위 테스트 세트가있는 경우 리팩토링 할 수 있습니다. 완전한 단위 테스트로 이해할 수없는 코드를 보거나들은 적이 없기 때문에 먼저 단위 테스트를 작성하고 해당 단위 테스트가 실제로 코드가 수행하는 작업을 나타내는 필수 사람들의 동의를 얻은 다음 코드를 변경합니다. . 나는 한두 번했다. 그것은 목에 통증이 있고 매우 비싸지 만 결국에는 좋은 결과를 낳습니다.


3

이해하기 어려운 방식으로 비교적 간단한 것을 수행하는 짧은 코드 조각이라면 확장 주석 및 / 또는 사용되지 않는 대체 구현에서 "빠른 이해"를 전환합니다.

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif

3

대답은 일반성을 잃지 않고 그렇습니다. 코드를 읽기 어려운 경우 항상 최신 코드를 추가하고 대부분의 경우 잘못된 코드를 삭제하십시오. 다음 프로세스를 사용합니다.

  1. 성능 테스트 및 지원 프로파일 링 정보를 찾으십시오. 성능 테스트가없는 경우 증거없이 주장 할 수있는 것은 증거없이 기각 될 수 있습니다. 현대 코드가 더 빠르다고 주장하고 이전 코드를 제거하십시오. 누군가 (심지어 자신조차도) 주장하면 프로파일 코드를 작성하여 어느 것이 더 빠르다는 것을 증명하도록 요청하십시오.
  2. 프로파일 링 코드가 존재하면 최신 코드를 작성하십시오. 같은 이름을 지정하십시오 <function>_clean(). 그런 다음 잘못된 코드와 코드를 "경주"하십시오. 코드가 더 좋으면 이전 코드를 제거하십시오.
  3. 이전 코드가 더 빠르면 현대 코드를 그대로 두십시오. 이 코드는 다른 코드의 용도에 대한 훌륭한 문서 역할을하며 "레이스"코드가 있으므로 두 경로 간의 성능 특성과 차이점을 문서화하기 위해 코드를 계속 실행할 수 있습니다. 코드 동작의 차이점에 대한 단위 테스트를 수행 할 수도 있습니다. 중요한 것은 현대 코드가 언젠가는 "최적화 된"코드를 능가 할 것이라는 점입니다. 그런 다음 잘못된 코드를 제거 할 수 있습니다.

QED.


3

죽기 전에 세상에 소프트웨어에 대해 한 가지를 가르 칠 수 있다면 "Performance vs X"는 False Dilemma라고 가르 칠 것입니다.

리팩토링은 일반적으로 가독성과 신뢰성의 이점으로 알려져 있지만 최적화를 쉽게 지원할 수 있습니다. 일련의 리팩토링으로 성능 개선을 처리하면 캠프장 규칙을 준수하면서 응용 프로그램의 속도를 높일 수 있습니다. 적어도 내 의견으로는, 윤리적으로 당신이 그렇게 할 의무가 있습니다.

예를 들어,이 질문의 저자는 미친 코드 조각을 발견했습니다. 이 사람이 내 코드를 읽는다면 미친 부분은 3-4 줄 길이라는 것을 알 수 있습니다. 메소드 자체에 있으며 메소드 이름과 설명은 메소드가 수행하는 작업을 나타냅니다. 이 방법에는 의심스러운 모양에도 불구하고 미친 코드가 올바른 답변을 얻는 방법을 설명하는 2-6 줄의 인라인 주석이 포함됩니다.

이러한 방식으로 구획화하면 원하는대로이 메소드의 구현을 자유롭게 교체 할 수 있습니다. 실제로, 그것은 아마도 내가 미친 버전을 쓴 방법 일 것입니다. 시도해 보거나 적어도 대안에 대해 물어보십시오. 대부분의 경우 순진한 구현이 눈에 띄게 나쁘다는 것을 알게 될 것입니다 (보통 2-10 배 개선을 위해 귀찮게합니다). 컴파일러와 라이브러리는 항상 바뀌고 있으며 오늘날에는 찾을 수 없었던 것을 누가 알 수 있습니까? 함수가 작성 되었습니까?


많은 경우 효율성의 주요 열쇠는 코드를 효율적으로 수행 할 수있는 방식으로 최대한 많은 작업을 수행하는 것입니다. .NET에 영향을 미치는 것 중 하나는 예를 들어 한 컬렉션의 일부를 다른 컬렉션으로 복사하는 효율적인 메커니즘이 없다는 것입니다. 대부분의 컬렉션은 큰 연속 항목 그룹 (전체가 아닌 경우)을 배열로 저장합니다. 예를 들어 50,000 개 항목 목록에서 마지막 5,000 개 항목을 복사하면 몇 번의 대량 복사 작업 (한 개가 아닌 경우)과 몇 가지 다른 항목으로 분해됩니다. 각 단계마다 최대 소수의 단계가 수행됩니다.
supercat

불행히도, 그러한 작업을 효율적으로 수행 할 수 있어야하는 경우에도 5,000 회 반복 ( "경우에 따라 45,000!") 동안 "bulky"루프를 실행해야하는 경우가 종종 있습니다. 작업을 벌크 배열 사본과 같은 것으로 축소 할 수있는 경우, 작업을 극도로 최적화하여 큰 성과를 거둘 수 있습니다. 각 루프 반복에서 12 단계를 수행해야하는 경우 특히 각 루프 반복을 최적화하기가 어렵습니다.
supercat

2

코드를 수정하는 것이 좋지 않을 수 있습니다. 코드가 성능상의 이유로 작성된 경우 변경하면 이전에 해결 된 성능 문제가 다시 발생할 수 있습니다.

당신이 경우 않는 읽기 쉽고 확장 될 상황을 타개하기로 결정 : 당신은 아래의 변화, 벤치 마크 이전 코드하기 전에 무거운 하중을. 이 이상한 코드가 해결 해야하는 성능 문제를 설명하는 오래된 문서 또는 문제 티켓을 찾을 수 있다면 더욱 좋습니다. 그런 다음 변경 한 후 성능 테스트를 다시 실행하십시오. 매우 다르지 않거나 여전히 허용 가능한 매개 변수 내에 있으면 괜찮을 것입니다.

때때로 시스템의 다른 부분이 변경 될 때이 성능 최적화 코드는 더 이상 이러한 강력한 최적화를 필요로하지 않지만 엄격한 테스트 없이는 특정 사항을 알 수있는 방법이 없습니다.


1
내가 함께 일하는 사람 중 하나는 사용자가 한 달에 한 번 자주 방문하는 영역의 항목을 최적화하는 것을 좋아 합니다. 코드를 작성하고 커밋하기를 좋아하고 QA 또는 다른 다운 스트림 기능이 실제로 테스트되도록하기 때문에 시간이 걸리고 자주 다른 문제가 발생하지 않습니다. : / 공평하게 말하면, 그는 일반적으로 빠르고 빠르며 정확합니다. 그러나 이러한 페니 앤티의 "최적화"는 팀의 나머지 부분을 어렵게 만들고 영구적 인 죽음은 좋은 일입니다.
DaveE

@DaveE : 이러한 최적화는 실제 성능 문제로 인해 적용됩니까? 아니면이 개발자가 할 수 있기 때문에 그것을 합니까? 최적화가 영향을 미치지 않을 것이라는 것을 알고 있다면 더 읽기 쉬운 코드로 안전하게 대체 할 수는 있지만 시스템 전문가 인 사람 만 신뢰할 수 있습니다.
FrustratedWithFormsDesigner 2012

그들은 할 수 있기 때문에 끝났습니다. 그는 실제로 약간의주기를 절약하지만, 프로그램 요소와의 사용자 상호 작용에 몇 초 (15-300 일)가 걸리면 "효율"을 추구하는 런타임의 10 분의 1 초를 면도하는 것은 어리석은 일입니다. 특히 그를 따르는 사람들이 자신이 한 일을 이해하기 위해 실시간으로 시간을 가져야 할 때. 이것은 원래 16 년 전에 구축 된 PowerBuilder 응용 프로그램이므로 사고 방식을 이해할 수 있지만 사고 방식을 현재 현실로 업데이트하는 것을 거부합니다.
DaveE

@DaveE : 나는 당신보다 당신과 함께 일하는 사람에 더 동의한다고 생각합니다. 아무 이유없이 느린 물건을 고칠 수 없다면 나는 미쳐 버릴 것입니다. 내가 열고는 / dev / urandom을 읽는 반복적으로 문자열을 조립하는 + 연산자를 사용하여 C ++의 선 또는 코드가 표시되는 경우 루프를 통해 때마다 누군가가 플래그를 설정 깜빡해서를, 나는 그것을 고칠. 다른 사람들이 한 번에 1 마이크로 초씩 미끄러지게했을 때 나는 이것에 대해 광신적이어서 속도를 계속 유지했습니다.
Zan Lynx

1
우리는 동의하지 않을 것에 동의해야합니다. 실제로 가끔 실행되는 함수에 대해 런타임에 분수 초를 절약하기 위해 한 시간을 소비 하고 다른 개발자를 위해 코드를 헤드 스크래칭 형태로 유지하는 것은 옳지 않습니다 ... 이 기능이 앱의 스트레스가 많은 부분에서 반복적으로 실행되는 기능이라면 fine & dandy입니다. 그러나 그것은 내가 묘사하는 경우가 아닙니다. 이것은 "UserX가 일주일에 한 번 정도 더 빠르게하는 것"이라고 말하는 것 외에는 다른 이유 없이도 코드를 잘못 사용하는 것입니다. 한편, 우리는해야 할 일을 지불하고 있습니다.
DaveE

2

여기서 문제는 "최적화 된"을 읽기 쉽고 확장 가능한 것과 구별하는 것입니다. 사용자가 최적화 된 코드로 보는 것과 컴파일러가 최적화 된 것으로 보는 것은 두 가지입니다. 변경하려는 코드가 전혀 병목 현상이되지 않을 수 있으므로 코드가 "간결한"경우에도 "최적화"할 필요는 없습니다. 또는 코드가 충분히 오래된 경우 컴파일러에서 기존의 코드보다 새롭고 간단한 내장 구조를 사용하여 최적화하는 최적화 기능이 내장되어있을 수 있습니다.

"가벼운"읽을 수없는 코드가 항상 최적화되는 것은 아닙니다.

나는 영리하고 희박한 코드가 좋은 코드라는 생각을 가지고 있었지만 때로는 코드 작성에 도움이되지 않고 언어의 모호한 규칙을 이용하여 상처를 입었습니다. 컴파일러가 영리한 코드를 임베디드 하드웨어에서 전혀 사용할 수없는 것으로 만들므로 영리해야합니다.


2

나는 성능에 타협 할 수 없기 때문에 Optimized 코드를 Readable 코드로 대체하지 않으며, 모든 섹션에서 적절한 주석을 사용하여 누구나 최적화 된 섹션에서 구현 된 논리를 이해할 수 있도록하여 문제를 모두 해결할 것입니다.

따라서 코드가 최적화되고 적절한 주석 처리 기능으로 코드를 읽을 수있게됩니다.

참고 : 적절한 주석을 사용하여 최적화 된 코드를 읽을 수있게 만들 수 있지만 읽을 수있는 코드를 최적화 된 코드로 만들 수는 없습니다.


한 사람이 코드를 편집하여 주석을 동기화 상태로 유지하는 것을 잊기 때문에이 접근법에 지칠 것입니다. 실제로 각각의 후속 검토는 실제로 Y를 수행하면서 X를 수행한다고 생각하면서 사라질 것입니다.
John D

2

다음은 간단한 코드와 최적화 된 코드의 차이점을 보여주는 예입니다. https://stackoverflow.com/a/11227902/1396264

대답의 끝으로 그는 단지 다음을 대체합니다.

if (data[c] >= 128)
    sum += data[c];

와:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

공정하게 말하면 if 문이 무엇으로 대체되었는지 전혀 알지 못하지만 응답자가 동일한 결과를 제공하는 비트 단위 연산을 말합니다 (나는 단지 그의 말을 받아 들일 것입니다) .

원래 시간의 1/4보다 짧은 시간에 실행됩니다 (11.54 초 대 2.5 초).


1

여기서 주요 질문은 : 최적화가 필요한가?

그렇다면 더 느리고 읽기 쉬운 코드로 바꿀 수 없습니다. 더 읽기 쉽게하려면 주석 등을 추가해야합니다.

코드를 최적화 할 필요가없는 경우 (가독성에 영향을 미치는 시점까지) 코드를 리팩토링하여 코드를 더 읽기 쉽게 만들 수 있습니다.

그러나 코드 변경 내용을 시작하기 전에 코드의 기능과 코드를 정확히 테스트하는 방법을 정확히 알고 있어야합니다. 여기에는 최대 사용량 등이 포함됩니다. 일련의 테스트 사례를 작성하지 않고 전후로 실행하지 않아도되는 경우 리팩토링을 수행 할 시간이 없습니다.


1

이것이 내가하는 방식입니다. 먼저 읽을 수있는 코드로 작동시킨 다음 최적화하십시오. 원본 소스를 유지하고 최적화 단계를 문서화합니다.

그런 다음 기능을 추가해야 할 때 읽을 수있는 코드로 돌아가서 기능을 추가하고 문서화 된 최적화 단계를 따릅니다. 문서화했기 때문에 새로운 기능을 사용하여 코드를 빠르고 쉽게 재 최적화 할 수 있습니다.


0

대부분의 경우 마이크로 최적화는 가치가 없으므로 IMHO 가독성은 최적화 된 코드보다 중요합니다.

넌센스 마이크로 최적화 에 관한 기사 :

우리 대부분은 에코로, ++ $ i를 $ i ++로, 또는 큰 따옴표를 작은 따옴표로 바꾸는 것과 같이 말도 안되는 미세 최적화에 대한 블로그 게시물을 읽는 데 지쳤습니다. 왜? 시간의 99.999999 %이므로 관련이 없습니다.

"print"는 실제로 무언가를 반환하기 때문에 "echo"보다 하나 이상의 opcode를 사용합니다. 에코가 인쇄보다 빠르다는 결론을 내릴 수 있습니다. 그러나 하나의 opcode는 비용이 들지 않습니다.

새로운 WordPress 설치를 시도했습니다. 스크립트가 랩톱에서 "버스 오류"로 끝나기 전에 중지되지만 opcode 수는 이미 230 만 개가 넘었습니다. 충분했다.


0

최적화는 상대적입니다. 예를 들면 다음과 같습니다.

BOOL 멤버가 많은 클래스를 생각해보십시오.

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

BOOL 필드를 비트 필드로 변환하려고 할 수 있습니다.

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

BOOL은 INT (Windows 플랫폼에서 부호있는 32 비트 정수)로 typedef되어 있으므로 16 바이트를 가져 와서 1 개로 압축합니다. 93 % 절약됩니다! 누가 그것에 대해 불평 할 수 있습니까?

이 가정 :

BOOL은 INT (Windows 플랫폼에서 부호있는 32 비트 정수)로 typedef되어 있으므로 16 바이트를 가져 와서 1 개로 압축합니다. 93 % 절약됩니다! 누가 그것에 대해 불평 할 수 있습니까?

으로 이끌다:

BOOL을 단일 비트 필드로 변환하면 3 바이트의 데이터가 절약되지만 멤버에 상수가 아닌 값이 할당되면 8 바이트의 코드 비용이 발생합니다. 마찬가지로 값을 추출하면 비용이 더 많이 듭니다.

예전에는

 push [ebx+01Ch]      ; m_sliced
 call _Something@4    ; Something(m_sliced);

된다

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 push ecx
 call _Something@4    ; Something(m_sliced);

비트 필드 버전은 9 바이트 더 큽니다.

앉아서 산술을하자. 이 비트 필드 필드 각각이 코드에서 6 번, 쓰기에 3 번, 읽기에 3 번 액세스된다고 가정하십시오. 코드 증가 비용은 약 100 바이트입니다. 옵티마이 저가 일부 연산에 대해 이미 레지스터에있는 값을 활용할 수 있기 때문에 정확히 102 바이트가 아닐 수 있으며 추가 명령어는 레지스터 유연성 감소 측면에서 숨겨진 비용을 가질 수 있습니다. 실제 차이는 더 많을 수도 있고 적을 수도 있지만 봉투 뒷면 계산의 경우 100이라고합시다. 한편 메모리 절약은 클래스 당 15 바이트였습니다. 따라서 손익분기 점은 7입니다. 프로그램이이 클래스의 인스턴스를 7 개 미만으로 작성하면 코드 비용이 데이터 절약을 초과합니다. 메모리 최적화는 메모리 최적화 해제였습니다.

참고 문헌

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.