open:google-common-lisp-style-guide

Google Common Lisp Style Guide

불필요한 메모리 할당을 피해야 합니다.

자동 저장소 관리 기능이 있는 언어(예: Lisp 또는 Java)에서 “메모리 누수”라는 구어적 표현은 실제로 필요하지 않은 저장소가 여전히 접근 가능하기 때문에 할당 해제되지 않는 상황을 의미합니다.

객체를 만들 때 더 이상 필요하지 않은 객체에 접근 가능한 상태로 두지 않도록 주의해야 합니다!

다음은 커먼 리스프에서 주의해야 할 함정입니다. 채우기 포인터가 있는 배열을 만들고 그 안에 객체를 넣은 다음 채우기 포인터를 다시 0으로 설정하면 해당 객체는 여전히 Lisp에서 도달할 수 있습니다(Common Lisp 사양에 따르면 채우기 포인터의 끝을 지나서 배열 항목을 참조해도 괜찮다고 명시되어 있습니다).

불필요하게 (즉, 할당하지 마세요). 가비지 컬렉션은 마법이 아닙니다. 과도한 할당은 일반적으로 성능 문제를 일으킵니다.

안전하지 않은 더 빠른 연산은 명확한 성능 요구가 있고 그 이유를 문서화할 수 있는 경우에만 사용해야 합니다.

Common Lisp 구현은 종종 안전하지 않은 방식으로 일부 연산을 더 빠르게 계산하기 위한 백도어를 제공합니다. 예를 들어 일부 라이브러리에서는 고정숫자만 사용하도록 설계된 산술 연산을 제공하며, 적절한 인수를 제공하면 올바른 결과를 더 빨리 산출합니다. 단점은 오버플로우가 발생할 경우 이러한 연산 결과가 올바르지 않으며, fixnum이 아닌 다른 값으로 호출될 때 정의되지 않은 동작이 발생할 수 있다는 것입니다.

일반적으로 안전하지 않은 연산은 인수가 올바른 유형이고 충분히 작은 것과 같은 일부 불변수를 만족하는 경우 동등한 안전 연산보다 더 빨리 올바른 결과를 산출하지만, 인수가 필요한 불변수를 만족하지 못하면 연산이 소프트웨어를 충돌시키거나 때로는 더 나쁜 경우 조용히 잘못된 답을 제공하는 등 정의되지 않은 동작을 가질 수 있습니다. 소프트웨어가 항공기나 기타 생명과 직결된 장치를 조종하는지, 아니면 거액의 돈을 처리하는지에 따라 이러한 정의되지 않은 동작은 사람을 죽이거나 파산시킬 수 있습니다. 그러나 적절한 속도는 때때로 사용할 수 없을 정도로 느린 소프트웨어와 제대로 작동하는 소프트웨어, 또는 순손실을 내는 소프트웨어와 수익을 낼 수 있는 소프트웨어의 차이를 만들 수 있습니다.

이러한 최적화의 필요성을 나타내는 프로파일링 결과와 해당 작업을 사용하는 것이 안전한 이유를 설명하는 세심한 문서가 모두 없는 상태에서 안전하지 않은 작업을 정의하거나 사용해서는 안 됩니다. 안전하지 않은 연산은 내부 함수로 제한해야 하며, 이러한 함수를 잘못된 인수와 함께 사용하는 것이 얼마나 안전하지 않은지 주의 깊게 문서화해야 합니다. 안전하지 않은 연산은 패키지 내부의 함수 내에서만 사용해야 하며, 잘못된 유형의 인수를 사용하여 함수를 호출하면 정의되지 않은 동작이 발생할 수 있으므로 선언의 사용법을 문서화해야 합니다. 패키지에서 내보낸 함수에 체크 타입을 사용하여 입력 인수를 살균하여 내부 함수에 잘못된 값이 전달되지 않도록 하세요.

일부 컴파일러에서는 일반적으로 유형 선언을 충분히 빠른 속도와 낮은 안전성을 가진 OPTIMIZE 선언과 결합하여 안전하지 않은 새로운 연산을 정의할 수 있습니다. 이러한 선언은 프로덕션 코드에 더 빠른 속도를 제공할 뿐만 아니라 유형 추론 기능이 있는 컴파일러에서 컴파일 타임에 버그를 찾는 데 검사 유형 어설션보다 더 유용할 수 있습니다. 이러한 컴파일러는 더 안전하고 느린 최적화 설정으로 전환하면 이러한 선언을 어설션으로 해석할 수 있으며, 이는 개발 중에 코드에서 동적 오류를 찾는 데는 좋지만 성능 트릭이라는 선언의 목적에 어긋나므로 프로덕션 코드에 사용해서는 안 됩니다.

적절한 경우 APPLY 대신 REDUCE를 사용해야 합니다.

첫 번째 연산자 인자의 의미가 동일한 경우에는 APPLY 대신 REDUCE를 사용해야 하며, 그렇지 않은 경우에는 컨센드업 리스트를 사용해야 합니다. 물론 원하는 작업을 수행하고 REDUCE가 수행하지 않는 경우에는 APPLY를 사용해야 합니다. 예를 들어

;; Bad
(apply #'+ (mapcar #'acc frobs))
;; Better
(reduce #'+ frobs :key #'acc :initial-value 0)

이 방법은 추가 컨싱을 수행하지 않고, 그 제한이 작은 구현에서 긴 목록에서 스택을 날려버릴 수 있는 CALL-ARGUMENTS-LIMIT를 초과할 위험이 없으므로 더 바람직합니다(코드의 무상 이식성을 피하고 싶기 때문입니다).

그러나 계산의 복잡성 클래스를 불필요하게 증가시키는 방식으로 REDUCE를 사용하지 않도록 주의해야 합니다. 예를 들어, 적절한 구현이 O(n)에 불과할 때 (REDUCE 'STRCAT …)는 O(n^2)입니다. 또한, (REDUCE 'APPEND …) 역시 :FROM-END T를 지정하지 않는 한 O(n^2)입니다. 이러한 경우 REDUCE를 사용해서는 안 되며, (APPLY 'STRCAT …) 또는 (APPLY 'APPEND …) 역시 사용해서는 안 됩니다. 대신 사용자에게 구현 세부 사항에 대한 부담을 주지 않으면서 이러한 경우를 적절히 처리하는 적절한 라이브러리(여러분이 기여해야 할 수도 있음)의 적절한 추상화를 사용해야 합니다. 예를 들어 UIOP:REDUCE/STRCAT을 참조하세요.


  • open/google-common-lisp-style-guide.txt
  • 마지막으로 수정됨: 2023/02/14 07:09
  • 저자 127.0.0.1