Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1_1.Beginner - Personal - How to Understand Performance Problems, 1_1.Beginner - Personal - How to Fix Performance Problems #4

Merged
merged 3 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 성능 문제를 이해하는 법
[//]: # (Version:1.0.0)
디버깅을 배우는 것처럼 실행 중인 시스템의 성능을 이해하는 법을 배우는 것은 피할 수 없는 일입니다. 설령 당신이 쓴 코드의 비용을 완벽하게 정확히 이해하고 있다고 하더라도, 그 코드는 당신에게 통제권이나 가시성이 거의 없는 소프트웨어 시스템을 호출할 것입니다. 그러나, 현실에서 성능 문제는 일반적인 디버깅과 조금 다르고 조금 더 쉽습니다.

당신이나 당신의 고객이 시스템이나 하부시스템이 너무 느리다고 생각한다고 가정해 봅시다. 그걸 더 빠르게 하기 전에, 왜 그것이 느린지에 대한 정신적 모델(mental model)을 세워야만 합니다. 이를 위해 프로파일링 도구나 잘 기록된 로그를 사용해서 시간이나 다른 리소스가 실제로 어디에 사용되고 있는지를 찾아낼 수 있습니다. 10%의 코드가 90%의 시간을 잡아먹는다는 유명한 격언이 있습니다. 저는 여기에 성능 문제에 대한 입출력 비용(I/O)의 중요성을 첨언하겠습니다. 대개 대부분의 시간은 어떤 식으로든 I/O에 사용됩니다. 값비싼 I/O나 10%의 비용이 많이 드는 코드를 찾아내는 것은 정신적 모델을 만드는 좋은 첫걸음입니다.

컴퓨터 시스템의 성능에는 많은 차원이 존재하며, 많은 종류의 리소스가 소비됩니다. 측정해야 하는 첫 번째 리소스는 *wall-clock time(벽시계 시간)*, 즉 계산하는 데에 걸리는 총 시간입니다. *wall-clock time*을 로깅 하는 것은 다른 프로파일링을 실행할 수 없을 때 발생하는 예측불가능한 상황에 대해 알려준다는 점에서 특히 귀중합니다. 하지만, 이것이 항상 모든 것을 알려주지는 않습니다. 때로 당신이 실제로 접할 컴퓨팅 환경에서 시간은 조금 더 걸리지만 프로세서를 많이 소모하지 않는 쪽이 훨씬 나을 수도 있습니다. 비슷하게, 메모리, 네트워크 대역폭, 데이터베이스 혹은 다른 서버 접근이 결론적으로는 프로세서 사용량보다 훨씬 비용이 많이 들 수도 있습니다.

동기화된 공유 리소스에 대한 경합은 교착 상태나 결핍을 만들어낼 수도 있습니다. 교착 상태(Deadlock)는 부적절한 동기화나 리소스 요구 때문에 진행이 불가능해지는 것입니다. 결핍(Starvation)은 구성요소를 적절하게 계획하는 데에 실패하는 것입니다. 이 모든 것이 예상 가능하다면, 프로젝트를 시작할 때부터 이러한 경합을 측정하는 방법을 두는 것이 최선입니다. 경합이 일어나지 않더라도, 확신을 가지고 이를 가정할 수 있다는 점에서 매우 유익합니다.

다음 장 [성능 문제를 해결하는 법](06-How-to-Fix-Performance-Problems.md)
13 changes: 13 additions & 0 deletions kr/1-Beginner/Personal-Skills/06-Fix-Performance-Problems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 성능 문제를 해결하는 법
[//]: # (Version:1.0.0)
대부분의 소프트웨어 프로젝트는 상대적으로 적은 노력으로도 처음 릴리스 되었을 때보다 10배에서 100배가량 빨라질 수 있습니다. 시장 출시일에 대한 압박 속에서는 작업을 단순하고 빠르게 만드는 해결책을 선택하는 것이 현명하면서도 효과적이지만, 다른 해결책들에 비해서는 덜 효율적이기도 합니다. 하지만, 성능 또한 사용성의 일부이며, 결국에는 더 신중하게 고려되어야 하는 경우가 많습니다.

매우 복잡한 시스템의 성능을 향상시키는 핵심은 그것을 충분히 분석해서 *병목 상태(bottlenecks)*, 즉 대부분의 리소스가 소비되는 지점을 찾는 것입니다. 계산 시간의 고작 1%를 차지하는 함수를 최적화하는 것은 큰 의미가 없습니다. 엄지의 법칙(rule of thumb)에 따라 무언가를 하기 전에 그것이 시스템 전체 혹은 대부분을 최소한 2배 이상 빠르게 만들 수 있는 것이 아니라면 신중하게 생각해보아야 합니다. 이를 위해 통상적으로 사용하는 방법이 있습니다. 당신이 만든 변화로 인해 필요한 테스트와 품질 보증에 드는 노력을 생각해보세요. 변경은 매번 테스트해야 하는 부담을 주기 때문에, 가끔 크게 변경하는 편이 훨씬 낫습니다.

당신이 두 배 이상의 성능 향상을 가져왔다면, 다시 고찰하고 분석해서 시스템에서 다음으로 가장 큰 병목 상태를 찾아내고 다시 두 배의 성능 향상을 가져올 수 있도록 공략해야 합니다.

자주, 성능의 병목 현상은 소를 셀 때 머리를 세는 대신 다리를 센 후 4로 나누는 방식과 같이 일어납니다. 예를 들어, 저는 관계형 데이터베이스 시스템에서 자주 조회하는 칼럼에 적절한 인덱스를 부여하지 않아서 최소 20배 이상 느려지게 만들었을 에러를 일으킨 적이 있습니다. 또 다른 예시로는 내부 루프에서 불필요한 I/O를 하거나, 디버깅할 때 사용한 더는 필요하지 않은 코드를 남겨놓거나, 불필요하게 메모리를 할당하거나, 성능에 관해서 문서화가 거의 되어 있지 않은 라이브러리나 하위 시스템을 서투르게 사용하는 경우 등이 있습니다. 이런 종류의 성능 향상은 *낮게 달린 과일(low-hanging fruit)* 이라 불리는데, 쉽게 해결할 수 있으면서 상당한 이점을 주기 때문입니다.

낮게 달린 과일이 거의 남지 않았을 때쯤엔 뭘 해야 하냐고요? 글쎄요, 더 높은 곳으로 손을 뻗거나 나무를 아예 베어낼 수도 있겠죠. 작은 성능 향상을 계속 이루거나 시스템이나 하위 시스템을 진지하게 재설계할 수도 있습니다(이는 새로운 디자인을 해야 할 뿐만 아니라 당신의 상사에게 이게 좋은 아이디어임을 설득해야 한다는 점에서 좋은 프로그래머가 되기 위한 스킬을 쌓을 멋진 기회입니다). 그러나, 하위 시스템 재설계를 주장하기 전에, 그 제안이 그것을 다섯 배에서 열 배 더 낫게 만들 수 있을지에 대해 자문해보아야 할 것입니다.

다음 장 [루프를 최적화하는 법](02-How-to-Optimize-Loops.md)
4 changes: 2 additions & 2 deletions kr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ Copyright 2002, 2003, 2016 Robert L. Read
- [How to Debug by Splitting the Problem Space](en/1-Beginner/Personal-Skills/02-How-to-Debug-by-Splitting-the-Problem-Space.md)
- [How to Remove an Error](en/1-Beginner/Personal-Skills/03-How-to-Remove-an-Error.md)
- [How to Debug Using a Log](en/1-Beginner/Personal-Skills/04-How-to-Debug-Using-a-Log.md)
- [How to Understand Performance Problems](en/1-Beginner/Personal-Skills/05-How-to-Understand-Performance-Problems.md)
- [How to Fix Performance Problems](en/1-Beginner/Personal-Skills/06-How-to-Fix-Performance-Problems.md)
- [성능 문제를 이해하는 법](ko/1-Beginner/Personal-Skills/05-How-to-Understand-Performance-Problems.md)
- [성능 문제를 해결하는 법](ko/1-Beginner/Personal-Skills/06-How-to-Fix-Performance-Problems.md)
- [How to Optimize Loops](en/1-Beginner/Personal-Skills/07-How-to-Optimize-Loops.md)
- [How to Deal with I/O Expense](en/1-Beginner/Personal-Skills/08-How-to-Deal-with-IO-Expense.md)
- [How to Manage Memory](en/1-Beginner/Personal-Skills/09-How-to-Manage-Memory.md)
Expand Down