깨끗하고 읽기 쉬운 코드와 빠른 읽기 어려운 코드 언제 선을 넘어야합니까?


67

코드를 작성할 때 항상 코드를 최대한 깨끗하고 읽기 쉽게 작성하려고합니다.

때때로 당신은 줄을 넘고 더 깨끗한 코드에서 더 추악한 코드로 이동해야 할 때가옵니다.

언제 그 선을 넘어도 괜찮습니까?


69
당신은, 당신이 선을 넘어 자신의 질문에 대답 당신은 선 교차 할 때
gnibbler

6
또한 "더티 코드"는 6 개월 후에 하드웨어에서 "클린 코드"만큼 빠르게 작동 할 수 있습니다. 그러나 Windows처럼 선상으로 가지 마십시오. :)
Mateen Ulhaq

21
이해하기 어려운 알고리즘과 이해하기 어려운 코드 간에는 상당한 차이가 있습니다. 때로는 구현해야하는 알고리즘이 복잡하고 복잡한 아이디어를 표현하기 때문에 코드가 혼동 될 수 있습니다. 그러나 코드 자체가 어려운 점이라면 코드를 수정해야합니다.
tylerl

8
많은 경우에 스마트 컴파일러 / 인터프리터는 깨끗하고 읽기 쉬운 코드를 최적화하여 "추악한"코드와 동일한 성능을 갖습니다. 프로파일 링에서 달리 언급하지 않는 한 변명의 여지가 거의 없습니다.
Dan Diplo

1
요즘 컴파일러의 경우, 추악한 코드는 깨끗한 코드와 동일 할 것입니다 (정말 이상한 일을하지 않는다고 가정). 특히 .NET에서는 변수를 정의하는 방법이 성능에 영향을 미치는 C ++ / MFC 시절과 다릅니다. 유지 관리 가능한 코드를 작성하십시오. 일부 코드는 복잡해 지지만 이것이 추악하다는 의미는 아닙니다.
DustinDavis

답변:


118

당신은 때 선을 넘어

  • 당신은 한 측정 코드가 있음을 의도 된 사용을 위해 너무 느린 .
  • 코드를 정리할 필요가없는 대체 개선 사항을 시도했습니다.

실제 예제는 다음과 같습니다. 실행중인 실험 시스템이 데이터를 너무 느리게 생성하여 실행 당 9 시간 이상 걸리고 CPU의 40 % 만 사용했습니다. 코드를 너무 많이 엉망으로 만드는 대신 모든 임시 파일을 메모리 내 파일 시스템으로 옮겼습니다. 추악하지 않은 코드의 8 줄을 추가했으며 이제 CPU 사용률이 98 % 이상입니다. 문제 해결됨; 추한 필요가 없습니다.


2
또한 참조 구현 및 하드웨어 변경 및 더 빠르고 해커 코드가 더 이상 작동하지 않을 때 원래의 느리고 더 깨끗한 코드를 유지해야합니다.
Paul R

4
@PaulR 어떻게 그 코드를 유지합니까? 의견의 형태로? 그것은 잘못되었습니다. imo-주석이 오래되어 아무도 읽지 않으며 개인적으로 코드를 주석 처리 한 코드를 보는 경우 일반적으로 제거합니다. 그것이 무엇을하는지 설명하는 방법에 대한 의견이 더 좋습니다.
Evgeni

5
@Eugene : 나는 보통 원래 버전의 루틴을 이름을 유지 foo하고 이름을 바꿉니다. foo_ref일반적으로 foo소스 파일에서 바로 위에 있습니다. 내 테스트 장치에서 나는 전화 foofoo_ref검증과 상대 성능 측정합니다.
Paul R

5
@Paul 당신이하고 있다면 최적화 된 버전이 ref 함수보다 느리다면 테스트에 실패하는 것이 좋습니다. 더 빠른 속도로 가정 한 가정이 더 이상 사실이 아닌 경우에 발생할 수 있습니다.
user1852503

58

거짓 이분법입니다. 당신은 코드를 빠르게 만들 수 있습니다 유지 관리가 용이.

당신이하는 방법은 특히 가능한 한 간단한 데이터 구조로 깨끗하게 작성하는 것입니다.

그런 다음 타임 드레인의 위치를 ​​알고 ( 이전에 작성한 , 이전이 아니라 실행하여 ) 하나씩 수정하십시오. (여기에 예가 있습니다.)

추가 : 우리는 항상 시간과 메모리 사이의 균형 또는 속도와 유지 보수성 간의 균형과 같은 균형에 대해 듣고 있습니까? 그러한 곡선이 존재할 수도 있지만, 주어진 프로그램이 곡선 위에 있거나 심지어 그 근처 있다고 가정해서는 안됩니다 .

곡선에있는 모든 프로그램은 (특정 종류의 프로그래머에게 제공함으로써) 훨씬 느리게 유지되고 유지 보수가 용이하지 않은 상태로 쉽게 만들어 질 수 있으며, 곡선 근처에는 아무 것도 없습니다. 그런 프로그램에는 더 빠르고 유지 보수가 용이 한 공간이 충분합니다.

내 경험상 많은 프로그램이 시작됩니다.


동의한다. 실제로 깨끗하지 않은 빠른 코드는 올바르게 수정할 수 없으므로 결국 느려질 것입니다.
edA-qa mort-ora-y

22
나는 그것이 거짓 이분법이라는 것에 동의 하지 않습니다 . IMO 는 분할이 매우 실제적인 라이브러리 코드 ( 응용 프로그램 코드 에는 그다지 중요하지 않음)의 시나리오가 있습니다 . 자세한 내용은 내 대답을 참조하십시오.
Marc Gravell

1
마크, 당신은 "링크"URL로 댓글에 답변에 연결할 수 있습니다. programmers.stackexchange.com/questions/89620/…

우리는 코드를 더 빠르게 만들기 위해 노력해야 할 모든 것을 발견했습니다. 그러나 최상의 솔루션을 찾기 위해 프로파일 러로 실험 한 후에 (코드가 못 생겼음) 이것이 코드가 못 생겨야한다는 것을 의미하지는 않습니다. 처음에는 분명해 보이지 않지만 일단 발견되면 일반적으로 깨끗하게 코딩 될 수있는 최상의 솔루션을 찾는 문제입니다. 따라서 나는 그것이 잘못된 이분법이며, 장난감으로 재미를 본 후에 방을 정리하지 않는 변명이라고 믿습니다. 나는 그것을 빨아 들여서 방을 정리한다고 말합니다.
Martin York

나는 그것이 이분법이라는 것에 동의하지 않습니다. 많은 그래픽 작업을 수행하기 때문에 나에게 가장 분명한 예는 타이트한 그래픽 루프입니다.이 작업이 얼마나 자주 수행되는지는 모르지만 C로 작성된 게임 엔진에서 코어 렌더링에 어셈블리를 사용하는 것이 일반적이었습니다. 매 마지막 속도 저하를 줄이기 위해 루프. 또한 파이썬으로 프로그래밍하지만 C ++로 작성된 모듈을 사용하는 상황을 생각하게합니다. "읽어야 할 내용"은 항상 상대적입니다. 속도를 높이기 위해 하위 언어로 넘어갈 때마다 해당 코드를 나머지 코드보다 읽기가 어렵습니다.
jhocking

31

내 OSS 존재에서 나는 성능을 겨냥한 많은 라이브러리 작업을 수행합니다. 이것은 발신자의 데이터 구조 (즉, 라이브러리 외부)와 밀접하게 관련되어 있으며 , 의도적으로 들어오는 유형에 대한 의무는 없습니다. 이 성능을 발휘할 수있는 가장 좋은 방법 은 메타 프로그래밍입니다. .NET-land에 있으므로 IL-emit을 의미합니다. 그것은 추악하고 못생긴 코드이지만 매우 빠릅니다.

이런 식으로, 나는 라이브러리 코드가 애플리케이션 코드 보다 "더 추악하다"는 것을 행복하게 받아들이고 , 입력에 대한 제어가 적거나 전혀 없기 때문에 단순히 다른 메커니즘을 통해 일부 작업을 수행해야한다고 주장 한다. 또는 다른 날에 그것을 표현했을 때 :

"광기의 절벽을 코딩 하기 때문에 "

이제 응용 프로그램의 "일반"(제정신) 개발자는 일반적으로 자신의 협업 / 전문 많은 시간을 투자하는 곳이기 때문에 코드는 약간 다릅니다; 각각의 목표와 기대는 (IMO) 약간 다릅니다.

IMO, 답변은 위가 빨리 될 수 있습니다 제안 하고 참조하고 유지하기 위해 쉽게 응용 프로그램 개발자가 데이터 구조를보다 효율적으로 제어 할 수 있고, 메타 프로그래밍 같은 도구를 사용하지 않는 코드입니다. 즉, 다른 수준의 광기와 다른 수준의 오버 헤드로 메타 프로그래밍을 수행하는 다른 방법이 있습니다. 이 분야에서도 적절한 추상화 수준을 선택해야합니다. 그러나 적극적으로 긍정적으로 생각할 때, 예상치 못한 데이터 를 절대적으로 가장 빠르게 처리하기를 원할 때 ; 잘 못 생길 수도 있습니다. 그것으로 처리; p


4
코드가 못 생겨서 유지가 불가능하다는 의미는 아닙니다. 주석과 들여 쓰기는 무료이며 추악한 코드는 일반적으로 관리 가능한 엔티티 (언어에 따라 클래스, 모듈, 패키지, 기능)로 캡슐화 할 수 있습니다. 코드는 똑같이 추악 할 수 있지만 적어도 사람들은 변경하려고하는 영향을 판단 할 수 있습니다.
tdammers

6
실제로 @tdammers이며 가능한 한 그렇게하려고합니다. 돼지에 립스틱을 바르는 것과 비슷합니다.
Marc Gravell

1
아마도 추악한 구문과 추악한 알고리즘을 구별해야 할 수도 있습니다. 추악한 알고리즘이 필요할 수도 있지만 추악한 구문은 일반적으로 변명 할 수없는 IMO입니다.
tdammers

4
@IMO 못생긴 구문은 일반적인 언어 수준 아래에 여러 수준의 추상화 가 있기 때문에 피할 수 없습니다 .
Marc Gravell

1
@marc ... 흥미 롭습니다. 메타 / 추상이 추악한 것에 대한 나의 첫 번째 반응은 특정 언어 / 플랫폼이이 둘을 연결하는 일부 기본 법보다는 메타 코딩에 도움이되지 않는다는 의심이었다. 제가 믿게 한 것은 대수 또는 콘크리트 산술보다 표현이 거의없는 집합 이론으로 끝나는 수학의 진보적 메타 레벨의 예라고 믿었습니다. 그러나 설정 표기법은 .... 다른 모두 언어와 모든 abstration 수준 아래에 자신의 언어가 아마
explorest

26

코드를 프로파일 링하고 실제로 심각한 속도 저하를 일으키는 지 확인한 경우.


3
그리고 "유의 한"무엇입니까?
Rook

2
@ hotpaw2 : 그것은 지능적인 답변입니다-개발자가 적어도 어느 정도 유능하다고 가정합니다. 그렇지 않으면, 기포 정렬보다 빠른 것을 사용하는 것이 (보통) 좋은 생각입니다. 그러나 너무 자주 (정렬을 유지하기 위해) 힙 정렬을 위해 퀵 정렬을 1 % 차이로 교체하고 같은 이유로 6 개월 후에 다른 사람이 다시 교환하는 것을보기 만합니다.

1
깨끗하지 않은 코드를 만들 이유 는 없습니다 . 효율적인 코드를 깨끗하고 유지 관리하기 쉽게 만들 수 없다면 뭔가 잘못한 것입니다.
edA-qa mort-ora-y

2
@SF. -고객은 더 빠를 수 있으면 항상 너무 느리다 것을 알게됩니다. 그는 코드의 "깨끗하지 않은"것을 신경 쓰지 않습니다.
Rook

1
@Rook : 고객은 (사소한) 인터페이스 코드가 너무 느릴 수 있습니다. 몇 가지 간단한 심리적 트릭은 실제로 코드를 가속화하지 않고 사용자 경험을 향상시킵니다. 버튼 이벤트를 즉시 작업을 수행하는 대신 백그라운드 루틴으로 연기하고, 진행률 표시 줄 또는 이와 유사한 것을 표시하고, 활동이 백그라운드에서 수행되는 동안 중요하지 않은 질문을합니다. ... 충분하지 않으면 실제 최적화를 고려할 수 있습니다.
SF.

13

클린 코드는 반드시 빠른 실행 코드와 배타적 일 필요는 없습니다. 일반적으로 읽기 어려운 코드는 더 빨리 실행되기 때문이 아니라 쓰기가 더 빠르기 때문에 작성되었습니다.

변경 사항이 실제로 무엇을 개선하는지 확실하지 않기 때문에 더 빠른 시도를 위해 "더러운"코드를 작성하는 것은 논란의 여지가 없습니다. Knuth가 가장 잘 설명했습니다.

"우리는 시간의 97 % 정도의 작은 효율성을 잊어야한다. 조기 최적화는 모든 악의 근원이다 . 그러나 우리는 그 중요한 3 %의 기회를 포기해서는 안된다. 좋은 프로그래머는 그런 것에 의해 만족에 빠지지 않을 것이다. 이유는, 그는 중요한 코드에서주의 깊게 볼 것이 현명 할 것입니다,하지만 그 코드가 확인 된 후에 만 ".

다시 말해, 코드를 먼저 깨끗하게 작성하십시오. 그런 다음 결과 프로그램을 프로파일 링하고 해당 세그먼트가 실제로 성능 병목 현상인지 확인하십시오. 그렇다면 필요에 따라 섹션을 최적화하고 최적화를 설명하기 위해 많은 문서 주석 (원본 코드 포함)을 포함 시키십시오. 그런 다음 결과를 프로파일 링하여 실제로 개선했는지 확인하십시오.


10

질문에 "빠른 코드 를 읽기 어렵다 "는 말이 있기 때문에 간단한 대답은 결코 없습니다. 읽기 어려운 코드 작성에 대한 변명의 여지가 없습니다. 왜? 두 가지 이유가 있습니다.

  1. 오늘 밤 집에가는 버스에 부딪히면 어떻게 되나요? 아니면 (보다 낙관적이고 더 일반적으로)이 프로젝트를 시작하고 다른 것으로 재할 당했습니까? 엉킨 코드의 혼란으로 당신이 상상 한 작은 혜택은 다른 사람이 그것을 이해할 수 없다는 사실에 의해 완전히 압도됩니다 . 이것이 소프트웨어 프로젝트에 미치는 위험은 과장하기 어렵습니다. 나는 주요 PBX 와 한 번 일했다제조업체 (사무실에서 일하는 경우 아마도 책상에 전화 중 하나가있을 것입니다). 프로젝트 관리자는 언젠가 자사의 핵심 제품 (표준 Linux 박스를 완전한 기능을 갖춘 전화 교환으로 전환 한 독점 소프트웨어)이 회사 내부에서 "블롭 (blob)"으로 알려져 있다고 말했다. 아무도 더 이상 그것을 이해하지 못했습니다. 그들은 새로운 기능을 구현할 때마다. 그들은 컴파일을 한 다음 뒤로 물러서서 눈을 감고 20까지 세고 손가락을 들여다보고 작동하는지 확인했습니다. 더 이상 제어하지 않는 핵심 제품이 필요한 비즈니스는 없지만 매우 일반적인 시나리오입니다.
  2. 그러나 나는 최적화해야한다! 자,이 질문에 대한 다른 답변에서 훌륭한 조언을 모두 따랐습니다. 코드가 성능 테스트 사례에 실패하고, 신중하게 프로파일 링하고, 병목 현상을 식별하고, 해결책을 제시했습니다 ... 비트 트위들 링 과 관련이 있습니다 . 좋아 : 이제 계속해서 최적화하십시오. 그러나 비밀은 다음과 같습니다 (그리고 이것을 원할 수도 있습니다). 소스 코드 크기의 최적화와 축소는 같은 것이 아닙니다.. 주석, 공백, 대괄호 및 의미있는 변수 이름은 모두 가독성을 높이는 데 큰 도움이됩니다. 컴파일러는 버릴 것이므로 비용이 전혀 들지 않습니다. (또는 JavaScript와 같이 컴파일되지 않은 언어를 작성하는 경우 JavaScript를 최적화해야하는 매우 유효한 이유가 있습니다 . 압축기 로 처리 할 수 ​​있습니다 .) 긴 줄처럼 비좁은 미니멀리즘 코드 여기에 게시 )는 최적화와 아무 관련이 없습니다. 프로그래머는 가능한 한 적은 수의 문자로 많은 코드를 포장하여 얼마나 영리한지를 보여 주려고 노력하고 있습니다. 그것은 영리하지 않고 바보입니다. 진정으로 영리한 프로그래머는 자신의 아이디어를 다른 사람에게 명확하게 전달할 수있는 사람입니다.

2
나는 대답이 "never"라는 것에 동의하지 않습니다. 일부 알고리즘은 본질적으로 효율적으로 이해 및 / 또는 구현하기가 매우 어렵습니다. 주석 수에 관계없이 코드를 읽는 것은 매우 어려운 과정 일 수 있습니다.
렉스 커

4

폐기 코드 인 경우. 나는 그 말 그대로 : 당신이 일회성 계산이나 작업을 수행하고, 그러한 확신 할 수있는 스크립트를 작성할 때 당신은 'RM 소스 파일'주저없이 할 수있는 다시 조치를 할 필요가 없을 것이다 다음 당신이 선택할 수 있습니다 못생긴 길.

그렇지 않으면 잘못된 이분법입니다. 더 빨리하기 위해 못생긴다고 생각하면 잘못하고있는 것입니다. (또는 좋은 코드가 무엇인지에 대한 당신의 원칙은 수정해야합니다. goto를 사용하는 것이 문제에 대한 적절한 해결책 일 때 실제로 매우 우아합니다. 그러나 거의 그렇지 않습니다.)


5
폐기 코드와 같은 것은 없습니다. "일회용 코드"가 "작동 중이고 코드를 다시 작성할 시간이 없기 때문에"코드를 제작할 때마다 1 페니를 지불했다면 백만장자가 될 것입니다. 여러분이 작성하는 모든 코드 라인은 다른 유능한 프로그래머가 오늘 밤 번개에 휩싸인 후에 내일 그것을 선택할 수 있도록 작성되어야합니다. 그렇지 않으면 쓰지 마십시오.
마크 휘태커

나는 그것이 거짓 이분법이라는 것에 동의하지 않습니다. IMO는 분할이 매우 실제적인 라이브러리 코드 (응용 프로그램 코드에는 그다지 중요하지 않음)의 시나리오가 있습니다. 자세한 내용은 내 대답을 참조하십시오.
Marc Gravell

@mark, 만약 "또 다른 유능한 프로그래머"가 실제로 유능하다면, 버리기 코드도 문제가되지 않아야합니다. :)

@ 마크-쉬움. 버리기 코드를 작성하면 수정 불가능한 방식으로 프로덕션 테스트에 실패 할 수 있습니다.
hotpaw2

@Mark, 만약 당신의“throw-away code”가 그것을 생산하게된다면 그것은 버려지는 코드가 아닙니다 . 말 그대로 버리는 코드에 대해 이야기하고 있음을 분명히하기 위해 대답에 시간이 걸렸 습니다. 즉, 첫 번째 사용 후 삭제하십시오. 그렇지 않으면 나는 당신의 정서에 동의하고 내 대답에서 많이 말했다.
maaku

3

시장에서 더 낮은 성능의 예상 비용이 해당 코드 모듈의 예상 코드 유지 관리 비용보다 클 때마다.

사람들은 여전히 ​​손으로 꼬인 SSE / NEON 등을 사용합니다. 올해의 인기있는 CPU 칩에서 경쟁사의 소프트웨어를 시도하고 이길 수있는 어셈블리.


좋은 비즈니스 관점, 때로는 프로그래머는 단순한 기술 이상의 것을 고려해야합니다.
this.josh

3

적절한 문서와 주석으로 읽기 어려운 코드를 이해하기 쉽게 만들 수 있습니다.

일반적으로 원하는 기능을 수행하는 읽기 쉬운 코드를 작성한 후 프로파일하십시오. 병목 현상으로 인해 더 복잡해 보이는 무언가를 수행해야 할 수도 있지만 스스로 설명하여 문제를 해결하십시오.


0

나에게 그것은 안정성의 비율입니다 (콘크리트에서 시멘트로, 오븐에서 구운 점토, 영구적으로 잉크로 쓰여진 돌로 설정). 미래에 코드를 변경해야 할 확률이 높을수록 코드가 불안정할수록 습식 점토와 같이 생산성을 유지하기가 더 쉬워집니다. 또한 가독성이 아니라 유연성을 강조합니다. 나에게 코드 변경의 용이성은 코드를 읽는 것보다 중요합니다. 코드를 쉽게 읽을 수 있고 악몽을 바꿀 수 있으며, 악의적 인 변경 사항 인 경우 구현 세부 사항을 읽고 이해하기 위해 어떤 용도로 사용할 수 있습니까? 그것이 학술적인 연습이 아닌 한, 일반적으로 프로덕션 코드베이스에서 코드를 쉽게 이해할 수 있다는 점은 필요에 따라 코드를 더 쉽게 변경할 수 있도록하는 것입니다. 변경하기 어려운 경우 가독성의 많은 이점이 창 밖으로 나옵니다. 가독성은 일반적으로 유연성의 상황에서만 유용하며, 유연성은 불안정성의 상황에서만 유용합니다.

당연히 코드를 읽는 것이 얼마나 쉬운 지 또는 어려운지에 관계없이 상상할 수있는 코드를 유지하기가 가장 어려운 경우에도 변경할 이유가없는 경우 문제를 일으키지 않고 사용하십시오. 그리고 특히 성능이 가장 중요시되는 저수준 시스템 코드의 경우 이러한 품질을 달성 할 수 있습니다. 나는 아직도 정기적으로 사용하는 C 코드를 가지고 있으며 80 년대 후반부터 변경되지 않았습니다. 그 이후로 변경할 필요가 없었습니다. 이 코드는 엉뚱한 날에 작성되었으며, 나는 거의 이해하지 못합니다. 그러나 오늘날에도 여전히 적용 가능하며 많은 활용을 위해 구현을 이해할 필요가 없습니다.

철저하게 테스트를 작성하는 것이 안정성을 향상시키는 한 가지 방법입니다. 다른 하나는 디커플링입니다. 코드가 다른 것에 의존하지 않는다면 코드를 변경하는 유일한 이유는 코드 자체가 변경되어야하기 때문입니다. 때로는 소량의 코드 복제가 디커플링 메커니즘의 역할을하여 안정성을 획기적으로 향상시켜 다른 코드와 완전히 독립적 인 코드를 얻는 경우 가치있는 트레이드 오프가 될 수 있습니다. 이제 그 코드는 외부 세계의 변화에 ​​무적입니다. 한편 10 개의 서로 다른 외부 라이브러리에 의존하는 코드는 앞으로 변경해야하는 10 배의 이유가 있습니다.

실제로 또 다른 유용한 것은 라이브러리를 코드베이스의 불안정한 부분과 분리하고, 제 3 자 라이브러리에서와 같이 개별 라이브러리를 빌드하는 것입니다 (적어도 아닙니다. 팀). 이러한 유형의 조직만으로도 사람들이 변조하지 못하게 할 수 있습니다.

또 다른 하나는 미니멀리즘입니다. 코드가 덜 노력할수록 코드가 잘 수행 할 수있는 가능성이 높아집니다. 모 놀리 식 디자인은 거의 영구적으로 불안정합니다. 점점 더 많은 기능이 추가 될수록 더 불완전 해 보입니다.

마이크로 튜닝 된 병렬 SIMD 코드와 같이 필연적으로 변경하기 어려운 코드를 작성하려고 할 때마다 안정성이 주요 목표가되어야합니다. 코드를 변경할 필요가 없기 때문에 코드를 유지 관리하는 데 어려움이 있으므로 향후 코드를 유지할 필요가 없습니다. 따라서 코드 유지 관리가 어려워도 유지 관리 비용이 0으로 줄어 듭니다.

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