Top

GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화

  • 원서명Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level (ISBN 1593270658)
  • 지은이랜달 하이드
  • 옮긴이이건호, 안병규, 박철현, 임중근, 심지웅
  • ISBN : 9788960770171
  • 30,000원
  • 2007년 07월 26일 펴냄
  • 페이퍼백 | 632쪽 | 188*250mm
  • 시리즈 : 프로그래밍 언어

판매처

개정판

책 소개

로우레벨로 생각하고 하이레벨로 코딩하자!

"우리는 지금 정말 효율적인 코드를 작성하고 있는 것일까? 어쩌면 이 책은 소프트웨어 제작에 대한 인식을 바꿔줄지도 모른다. 저자는 이 책이 '혁명적인 개념'을 가르치는 것은 아니라고 주장하지만, 초보자 수준을 넘어선 고급언어 프로그래머들은 랜달 하이드의 가르침에 반드시 주목해야 할 것이다. - UnixReview.com

『GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』는 고급언어로 작성된 소스 코드가 컴파일러를 거쳐 어떤 식의 기계어 코드로 바뀌는지에 대해 집중적으로 다루고 있다. 우리가 흔히 사용하는 자료 구조나 상수, 변수가 실제로는 어떻게 표현되는지, 연산이나 제어 구조가 기계어로는 어떻게 구현되는지, 컴파일러는 어떤 식으로 최적화를 수행하는지 심도 있게 다룸으로써 코드상에서 나타나는 약간의 차이가 컴파일된 후의 기계어 코드에서는 얼마나 큰 차이가 되어 돌아오는지를 이해하기 쉽게 설명해준다. 컴파일러가 어떻게 동작하는지를 이해하면 아름다운 기계어 코드로 변환되는 훌륭한 소스 코드를 작성하는 것이 가능해진다.

[ 이 책의 주요 내용 ]

■ 컴파일러의 출력물을 분석하는 방법을 익혀서 우리가 만든 코드가 좋은 기계어 코드를 제대로 만들어내는지 확인할 수 있다
■ 일반적인 제어 구조에 대해 컴파일러가 어떤 기계어 코드를 만들어내는지를 익혀서 고급언어 코드를 작성할 때 최선의 선택을 할 수 있다
■ 컴파일러 출력물을 읽는 데 필요한 80x86과 파워PC 어셈블리를 배울 수 있다
■ 컴파일러가 각종 상수나 변수를 기계어 데이터로 어떻게 변환하는지를 익혀서 더 빠르고 더 작은 프로그램을 만드는 데 활용할 수 있다


[ 이 책의 특징과 구성 ]

이 책은 현대의 컴파일러들이 효율적인 기계어로 번역하기에 적절한 고급언어(HLL, high-level language)의 구문을 어떻게 선택해야 할지 가르쳐준다. 대부분의 HLL에서는 같은 결과를 얻기 위해서 선택할 수 있는 여러 가지 코드가 존재한다. 그리고 기계어 수준에서 보면 이들 중에서 근본적으로 다른 것들보다 뛰어난 방법이 존재하기 마련이다. 더 효율적인 코드보다 덜 효율적인 코드를 선택할만한 이유가 존재할 수도 있지만(예를 들어 가독성을 위해서), 실제로는 대부분의 소프트웨어 엔지니어들은 HLL 구문들의 실제 수행 성능에 대해서 무지하다. 이러한 지식 없이 그들이 교육된 방법만으로는 올바른 구문을 선택할 수 없다. 이 책의 목표는 바로 그 점을 바꾸는 것이다.
효율적인 코드를 작성하기 위해서 전문적인 어셈블리 언어 프로그래머가 될 필요는 없지만, 컴파일러의 결과물을 검토할 생각이라면(실제로 이 책에서 하게 될 것이다) 최소한 어셈블리 언어 코드를 이해할 수 있는 지식은 있어야 한다. 3장과 4장은 80x86과 파워PC의 어셈블리 언어에 대한 간략한 입문서가 될 것이다.
5장과 6장에서는 컴파일러의 결과물을 통해서 HLL 구문들의 성능을 측정하는 것에 대해 배울 것이다. 이 장들은 어셈블리 언어를 검토하기 위해 역어셈블러, 목적 코드 덤프 툴, 디버거, 다양한 HLL 컴파일러 옵션과 여러 소프트웨어를 소개할 것이다.
책의 나머지 부분인 7장에서부터 15장까지는 컴파일러가 여러 가지 HLL 구문과 데이터 형식에 대해서 어떤 기계어 결과물을 만들어내는지 설명할 것이다. 이 지식을 이용하면 효율적인 애플리케이션을 제작하기 위해서 가장 적합한 데이터 형식, 상수, 변수, 제어문을 선택할 수 있게 될 것이다.

저자/역자 소개

[ 저자 소개 ]

랜달 하이드
최고의 어셈블리 언어 관련 서적인『The Art of Assembly Language』, 『GREAT CODE 제1권』(모두 No Starch Press)의 저자다. 또한『Waite Group’s MASM 6.0 Bible』의 공동 저자이기도 하다. 그는 닥터 돕스 저널(Dr.Dobb’s Journal)과 바이트(Byte)지를 비롯한 여러 전문 잡지에 기고하고 있다.


[ 저자 서문 ]

최고의 코드에 대한 평가 기준은 한 권의 책에서 모두 다루기에는 너무 많을 정도로 다양하다. 그러므로 『GRATE CODE』 시리즈 제2권인 이 책에서는 중요한 기준 중 하나인 “성능”에 대한 논의에 집중할 것이다. 컴퓨터 시스템의 성능이 MHz에서 수백 MHz로, 또 다시 GHz로 향상되면서 컴퓨터 소프트웨어의 성능에 대한 관심은 뒷전이 되었다. 오늘날 “절대로 코드 최적화 작업을 해서는 안 된다!”라고 주장하는 소프트웨어 엔지니어를 접하는 것은 전혀 낯선 일이 아니게 되었다. 하지만 우습게도 일반 사용자들은 그렇게 생각하지 않는다.
이 책은 효율적인 코드를 작성하는 방법을 설명하고 있지만, 일반적으로 생각하는 좁은 의미의 최적화에 대한 책은 아니다. 흔히 말하는 최적화는 소프트웨어 개발의 최종 단계에서 엔지니어들이 왜 자신들의 코드가 성능 명세에 부합하지 않는지 분석하고 그 사양을 만족하도록 코드를 수정하는 과정이다. 하지만 불행히도 최적화 단계 이전까지 성능에 대한 고려가 빠져 있었다면 최적화 과정이 실질적인 성능 향상으로 이어질 가능성은 거의 없다. 애플리케이션이 적당한 성능을 갖도록 보장하는 작업은 개발의 첫 단계인 설계와 구현에서 이루어져야 한다. 최적화는 시스템의 성능을 미세하게 향상시키지만 기적을 불러일으키는 경우는 거의 없다.
“설익은 최적화는 모든 악의 근원이다”라는 문구를 인용할 때 흔히 도날드 커누스(Donald Knuth)를 언급하지만, 처음으로 이 말을 꺼낸 사람은 토니 호어(Tony Hoare)다. 이 문장은 흔히 개발의 마지막 단계(흔히 경제적 원인 혹은 출시일을 이유로 최적화가 생략되는 단계) 때까지 최적화를 기피하는 엔지니어들의 슬로건으로 사용되어 왔다. 하지만 호어는 “애플리케이션 개발의 초기 단계에서 성능에 대한 고려를 하는 것은 모든 악의 근원이다”라고 말하지는 않았다. 그가 명시한 “설익은 최적화”라는 표현은 그 당시에 어셈블리 언어로 작성된 코드의 명령어 개수와 사이클 수를 세는, 코드의 기반이 유동적인 초기 단계에서 하기에는 적당하지 않은 종류의 최적화를 의미했다. 그러므로 호어의 주장은 적절한 것이었다. 찰스 쿡(Charles Cook)의 짧은 에세이(www.cookcomputing.com/blog/archives/000084.html)로부터 인용한 다음 설명은 이 문구를 잘못 받아들였을 때의 문제점을 간략하게 설명하고 있다.

“나는 항상 이 문구가 너무 자주 원래 의도와는 다른 영역에 적용되면서 소프트웨어 디자이너들이 실수하도록 이끌고 있다고 생각해왔다.
이 문구의 완전한 본래 문장은 “말하자면 97% 정도의 시간 동안에는 미세한 부분의 성능 효율에 대해서는 잊고 있어야 한다. 설익은 최적화는 모든 악의 근원이다”이고, 나는 이에 동의한다. 보통 전체 성능의 병목지점이 어디인지 분명해지기 전까지 미세한 최적화에 신경 쓰는 것은 시간의 낭비이다. 하지만 반대로 시스템 레벨의 소프트웨어를 디자인할 때는 처음부터 성능에 대한 고려를 항상 염두에 두어야 한다. 우수한 소프트웨어 개발자는 발달된 감지 능력을 이용해서 성능 측면에서 문제를 일으킬만한 부분을 자동적으로 고려하게 되어 이를 잘 수행한다. 미숙한 개발자들은 이후 단계에서의 약간의 미세 조정으로 어떤 문제도 해결할 수 있을 것이라는 잘못된 믿음으로 인해 이를 무시한다.”

토니 호어가 진정으로 의미한 것은, 소프트웨어 엔지니어들은 하나의 문장이 몇 개의 CPU 사이클을 소요하는지 등을 세는 전통적 의미의 최적화가 아닌 다른 요소들, 예를 들어 좋은 알고리즘 디자인과 그 알고리즘에 대한 우수한 구현 등에 대해서 더 신경을 써야 한다는 것이다.
경험이 많은 소프트웨어 엔지니어라면 이러한 부분적인 구현 기술은 성능 측면에서 별 차이를 가져오지 못한다고 주장할 수도 있을 것이다. 어떤 측면에서는 그러한 평가가 옳을 수도 있다. 하지만 우리는 이렇듯 미세한 차이점이 누적된다는 것을 기억해야 한다. 이 책에 소개된 기술들을 사용하지 않는다고 하더라도 여전히 가독성이 떨어지고 유지보수가 더 힘든 코드를 작성할 가능성도 존재한다는 점이 분명한 이상, 기능(시스템 디자인의 관점에서)을이 같은 두 종류의 코드가 주어졌을 때 성능이 더 우수한 쪽을 선택해야 한다는 것은 명백하다. 하지만 불행히도 오늘날의 많은 소프트웨어 엔지니어들은 서로 다른 방식으로 구현된 두 가지 코드가 주어졌을 때 어느 쪽이 더 우수한지 구별할 수 있는 능력이 부족하다.
이 책에서 소개된 많은 개념들은 최적화 단계에서 적용하는 것도 가능하지만, 대부분의 기술들은 최초의 코딩 단계에서부터 고려되어야 한다. 코드를 완전히 작성할 때까지 이런 부분들에 대한 신경을 끄고 있다가는 그 이후에 이런 개념들을 적용하기는 매우 힘들 것이다. 완성된 코드에 이 개념들을 구현하는 것 자체가 너무 많은 노력을 필요로 하기 때문이다.
책을 읽는 동안 호어 박사의 “설익은 최적화는 모든 악의 근원이다”라는 문구를 꼭 명심해야 한다. 이 책에 소개된 정보를 잘못 적용하여 읽기 힘들고 유지보수가 힘든 코드를 작성하는 경우도 분명히 생길 것이다. 이런 실수는 특히 코드가 유동적이고 변경될 가능성이 높은 프로젝트의 초기 구현 과정에서 더욱 큰 악재가 된다. 하지만 이 책이 다른 것은 모두 무시하고 가장 효율적인 구문을 선택하는 것만을 목표로 하지 않는다는 것을 명심해야 한다. 이 책의 관심사는 다양한 HLL 구문이 발생시키는 비용을 이해하여 선택권이 주어졌을 때 더 효율적이고 올바른 방법을 선택할 수 있도록 하는 것이다. 어떤 경우에는 효율이 좀 떨어지는 코드를 선택해야 하는 정당한 이유가 존재한다. 하지만 주어진 구문의 비용을 이해하지 못한다면 효율적인 대안을 선택할 방법 자체가 존재하지 않는 것이다.


[ 역자 소개 ]

이건호
서울대학교 컴퓨터공학과 학부와 석사를 졸업했다. 각종 경시대회 참가를 취미로 삼다 또 다른 취미인 게임을 직접 만들기로 하고 게임업계에 투신. 엔씨소프트에서 리니지2 서버 프로그래머 등으로 6년 여간 열심히 일하다가 뒤늦게 UC버클리에서 공부를 다시 시작하기 직전에 번역을 마쳤다.

안병규
서울대학교 컴퓨터공학과 졸업. 서울과학고 재학시절 국제정보올림피아드 금메달 및 한국정보올림피아드 금상을 수상하였으며, 대학교 재학시절 게임업계에 투신하여 (주)넥슨에서 크레이지 아케이드, 빅샷을 개발했고 (주)제이투엠에서 레이시티를 개발했다. 현재 서울시 청담동에 거주하며 인생 제2의 도약을 준비하는 중이다.

박철현
한국과학기술원 전산학과 학부를 마치고 서울대학교 컴퓨터공학부에서 석사과정으로 네트워크를 공부하고 있다. 빠른 은퇴와 유유자적한 삶을 꿈꾸며 연구와 프로젝트에 매진하고 있다.

임중근
한국과학기술원 전산학과 학부와 석사를 졸업했다. 다수의 프로그래밍 컨테스트에 참가해 수상한 경력이 있는 슈퍼 프로그래머이다. 주 언어는 파스칼과 C++이다.

심지웅
9살 때 LOGO 언어를 시작으로 프로그래밍의 세계에 발을 들여놓은 후 국제정보올림피아드와 ACM ICPC World Final에 진출했다. 서울대학교 동기들과 함께 게임 개발사 헥스플렉스 엔터테인먼트를 창업, “더 보스”로 2003 대한민국 게임대상 최우수상을 수상했고, 현재는 미국의 University of Pennsylvania에서 컴퓨터공학 석사과정을 밟으며 인공지능 로봇 연구라는 새로운 목표에 도전 중이다.


[ 역자 서문 ]

작고 빠른 프로그램은 곧 어셈블리로 작성된 프로그램이라는 등식이 성립하던 시절이 있었다. 제한된 메모리와 느린 CPU를 최대한 활용하기 위해 처음부터 끝까지 무엇을 어떻게 할지 명령을 내려야 했던 것이다. 고급언어로 실제 애플리케이션을 만드는 것이 일반화된 후에도 상당한 기간 동안 가장 중요한 부분은 어셈블리로 만들어지는 것이 보통이었다. 그러나 메모리 용량과 CPU의 속도가 끊임없이 향상되어 오늘날에는 어셈블리 코딩을 하는 경우를 거의 볼 수 없게 되었다.
프로그램의 성능은 근본적으로 어떤 알고리즘을 사용하였느냐에 따라 결정된다. 어떤 알고리즘을 쓰느냐에 따라 실행되자마자 결과가 나올 수도 있고 지구가 멸망할 때까지 결과를 보지 못할 수도 있다. 그러나 같은 알고리즘을 쓰더라도 어떻게 구현했느냐에 따라 실제 성능은 크게 차이가 나게 된다. 덧셈 한 번을 CPU 사이클 하나에 끝낼 수도 있고, 여기저기 메모리를 참조하면서 쓸데없는 작업을 하다가 훨씬 늦게 끝낼 수도 있다. 이런 것들이 쌓이게 되면 최종적인 성능은 하늘과 땅 차이가 된다. 고급언어를 사용하면서도 중요한 부분은 어셈블리로 작성하던 것은 바로 이 때문이었다.
같은 알고리즘에 대해 최대한 효율적으로 구동되는 프로그램을 만들기 위해서는 결국 컴퓨터 하드웨어가 어떤 원리로 동작하는지에 대해 이해할 필요가 있다.『GREAT CODE 제1권 하드웨어의 이해』에서는 컴퓨터의 동작 원리에 대해 배울 수 있었다. 그렇다면 이제 어셈블리를 익혀서 열심히 최적화를 해야 할까? 물론 그것은 답이 아니다. 요즘 만들어지는 애플리케이션은 예전에 비해 훨씬 많은 일을 한다. 수많은 부분을 일일이 어셈블리로 작성하는 것은 이제 수지가 맞지 않는다. 다행히 최근의 컴파일러는 예전보다 훨씬 똑똑해졌다. 맨손으로 덤비는 것보다는 좋아진 도구를 잘 이용하는 것이 훨씬 효과적일 것이다. 따라서 우리가 해야 할일은 주어진 도구 즉, 컴파일러가 어떤 일을 하는지 제대로 이해하는 것이다.
이 책,『GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』는 고급언어로 작성된 소스 코드가 컴파일러를 거쳐 어떤 식의 기계어 코드로 바뀌는지에 대해 집중적으로 다루고 있다. 우리가 흔히 사용하는 자료 구조나 상수, 변수가 실제로는 어떻게 표현되는지, 연산이나 제어 구조가 기계어로는 어떻게 구현되는지, 컴파일러는 어떤 식으로 최적화를 수행하는지 심도 있게 다룸으로써 코드상에서 나타나는 약간의 차이가 컴파일된 후의 기계어 코드에서는 얼마나 큰 차이가 되어 돌아오는지를 이해하기 쉽게 설명해준다.
저자도 강조하고 있지만 이 책의 목적은 어셈블리를 배워서 활용하자는 것이 아니다. 고급언어 코드가 어떻게 어셈블리 혹은 기계어로 변환되는지를 이해하고, 이를 바탕으로 좀 더 효율적인 고급언어 코드를 작성할 수 있는 능력을 길러주는 책이다. 이 책을 통해 내가 작성한 코드가 왜 느린지, 그리고 어떻게 해야 좀 더 좋은 코드를 만들어낼 수 있는지에 대해 확실하게 감을 잡을 수 있을 것이다. 또한 컴퓨터 구조와 컴파일러 이론을 배운 사람들도 어렴풋하게 기억나는 내용을 깔끔하고 구체적으로 정리할 수 있을 것이다. 독자 여러분이 하드웨어의 성능과 컴파일러의 기능을 충분히 활용하여 최고의 코드를 작성하는 데 이 책이 큰 도움이 될 것으로 기대한다.

이건호

목차

목차
  • 1장 로우레벨을 고려한 프로그램 최적화 1
    • 1.1 컴파일러 품질에 대한 오해 2
    • 1.2 어셈블리 언어를 배워야 하는 이유 2
    • 1.3 어셈블리 프로그래머가 될 필요는 없다 3
    • 1.4 로우레벨에 대한 고려 3
      • 1.4.1 받은 만큼만 돌려주는 컴파일러 4
      • 1.4.2 컴파일러가 좋아하는 코드의 특성 4
      • 1.4.3 어셈블리를 고려한 고급언어 코딩 방법 5
    • 1.5 고급언어 코딩 7
    • 1.6 이 책을 읽기 위해 필요한 사전 지식 7
    • 1.7 언어 중립적인 접근 방법 8
    • 1.8 최고의 코드가 가지는 특징 8
    • 1.9 동작 환경 9
    • 1.10 참고 자료 10
  • 2장 어셈블리 언어를 꼭 배워야 할까? 11
    • 2.1 어셈블리 언어는 배우기 어렵다 11
    • 2.2 해결책: GREAT CODE 제2권 12
    • 2.3 해결책: 고급 어셈블러 13
    • 2.4 고급 어셈블러 HLA 14
    • 2.5 하이레벨로 생각하고 로우레벨로 코딩하기 15
    • 2.6 로우레벨 프로그래밍 패러다임 16
    • 2.7 유용한 참고 문헌 19
  • 3장 고급언어 프로그래머를 위한 80x86 어셈블리 21
    • 3.1 배워두면 도움되는 어셈블리 언어 21
    • 3.2 80x86 어셈블리 문법 22
    • 3.3 기본 80x86 아키텍처 23
      • 3.3.1 레지스터 23
      • 3.3.2 80x86 범용 레지스터 24
      • 3.3.3 80x86 EFLAGS 레지스터 25
    • 3.4 리터럴 상수 26
      • 3.4.1 이진 리터럴 상수 26
      • 3.4.2 십진 리터럴 상수 27
      • 3.4.3 16진 리터럴 상수 27
      • 3.4.4 문자와 문자열 리터럴 상수 28
      • 3.4.5 실수 리터럴 상수 29
    • 3.5 선언(기호) 상수 30
      • 3.5.1 HLA에서의 선언 상수 30
      • 3.5.2 Gas에서의 선언 상수 30
      • 3.5.3 MASM과 TASM에서의 선언 상수 31
    • 3.6 80x86 주소 지정 방식 31
      • 3.6.1 80x86 레지스터 주소 지정 방식 31
      • 3.6.2 즉시 주소 지정 방식 33
      • 3.6.3 직접 메모리 주소 지정 방식 33
      • 3.6.4 레지스터 간접 주소 지정 방식 35
      • 3.6.5 인덱스 주소 지정 방식 36
      • 3.6.6 스케일 인덱스 주소 지정 방식 38
    • 3.7 데이터 선언 40
      • 3.7.1 HLA에서의 데이터 선언 40
      • 3.7.2 MASM과 TASM에서의 데이터 선언 41
      • 3.7.3 Gas에서의 데이터 선언 41
    • 3.8 오퍼랜드 크기 지정 44
      • 3.8.1 HLA에서의 타입 지정 44
      • 3.8.2 MASM과 TASM에서의 타입 지정 45
      • 3.8.3 Gas에서의 타입 지정 45
    • 3.9 80x86 기본 명령어 셋 46
    • 3.10 참고 자료 46
  • 4장 고급언어 프로그래머를 위한 파워PC 어셈블리 47
    • 4.1 배워두면 도움되는 어셈블리 언어 48
    • 4.2 어셈블리 문법 48
    • 4.3 기본 파워PC 아키텍처 48
      • 4.3.1 범용 정수 레지스터 49
      • 4.3.2 범용 실수 레지스터 49
      • 4.3.3 유저 모드용 특수 목적 레지스터 49
    • 4.4 리터럴 상수 52
      • 4.4.1 이진 리터럴 상수 52
      • 4.4.2 십진 리터럴 상수 53
      • 4.4.3 16진 리터럴 상수 53
      • 4.4.4 문자와 문자열 리터럴 상수 53
      • 4.4.5 실수 리터럴 상수 53
    • 4.5 선언(기호) 상수 54
    • 4.6 파워PC 주소 지정 방식 54
      • 4.6.1 파워PC 레지스터 접근 54
      • 4.6.2 즉시 주소 지정 방식 54
      • 4.6.3 파워PC 메모리 주소 지정 방식 55
    • 4.7 데이터 정의 57
    • 4.8 오퍼랜드 크기 지정 59
    • 4.9 기본 명령어 셋 59
    • 4.10 참고 자료 60
  • 5장 컴파일러 동작과 코드 생성 61
    • 5.1 프로그래밍 언어가 사용하는 파일 유형 62
    • 5.2 프로그래밍 언어 소스 파일 62
      • 5.2.1 토큰화 형식 소스 파일 62
      • 5.2.2 특화된 소스 파일 포맷 63
    • 5.3 컴퓨터 언어 처리기의 유형 63
      • 5.3.1 순수 인터프리터 63
      • 5.3.2 인터프리터 64
      • 5.3.3 컴파일러 64
      • 5.3.4 증분 컴파일러 65
    • 5.4 번역 과정 66
      • 5.4.1 어휘 분석과 토큰 67
      • 5.4.2 파싱(구문 분석) 69
      • 5.4.3 중간 코드 생성 70
      • 5.4.4 최적화 70
      • 5.4.5 컴파일러 옵션 비교 81
      • 5.4.6 네이티브 코드 생성 81
    • 5.5 컴파일러의 출력물 81
      • 5.5.1 고급언어 코드로 된 컴파일러 출력 82
      • 5.5.2 어셈블리 언어로 된 컴파일러 출력 83
      • 5.5.3 오브젝트 파일로 된 컴파일러 출력 84
      • 5.5.4 실행 파일로 된 컴파일러 출력 85
    • 5.6 오브젝트 파일 포맷 85
      • 5.6.1 COFF 파일 헤더 86
      • 5.6.2 COFF 옵션 헤더 88
      • 5.6.3 COFF 섹션 헤더 91
      • 5.6.4 COFF 섹션 93
      • 5.6.5 재배치 섹션 93
      • 5.6.6 디버깅과 심벌 정보 94
      • 5.6.7 오브젝트 파일 포맷에 대해 더 공부하려면 94
    • 5.7 실행 파일 포맷 94
      • 5.7.1 페이지, 세그먼트, 파일 크기 95
      • 5.7.2 내부 단편화 97
      • 5.7.3 공간 최적화를 하는 이유 98
    • 5.8 오브젝트 파일 안의 데이터와 코드 정렬 99
      • 5.8.1 섹션 정렬 크기 선택 100
      • 5.8.2 섹션 연결 101
      • 5.8.3 섹션 정렬 값 제어 101
      • 5.8.4 섹션 정렬 값과 라이브러리 모듈 102
    • 5.9 링커와 링커가 코드에 주는 영향 110
    • 5.10 참고 자료 113
  • 6장 컴파일러 출력물 분석 툴 115
    • 6.1 배경 지식 116
    • 6.2 컴파일러에서 어셈블리 코드를 출력하게 하는 법 117
      • 6.2.1 GNU와 볼랜드 컴파일러의 어셈블리 코드 출력물 117
      • 6.2.2 비주얼 C++의 어셈블리 코드 출력물 118
      • 6.2.3 어셈블리 코드 출력물 예제 118
      • 6.2.4 컴파일러의 어셈블리 코드 출력물 분석 128
    • 6.3 컴파일러 출력물 분석을 위한 오브젝트 코드 유틸리티 129
      • 6.3.1 마이크로소프트의 dumpbin.exe 유틸리티 129
      • 6.3.2 FSF/GNU의 objdump.exe 도구 142
    • 6.4 컴파일러 출력물 분석을 위한 역어셈블러 사용법 146
    • 6.5 컴파일러 출력물 분석을 위한 디버거 사용법 149
      • 6.5.1 통합 환경에 내장된 디버거를 사용하는 법 150
      • 6.5.2 독립 디버거를 사용하는 법 152
    • 6.6 컴파일러 출력물 비교 153
      • 6.6.1 diff를 이용한 코드의 수정 전/후 비교 153
      • 6.6.2 수작업을 통한 비교 162
    • 6.7 참고 자료 163
  • 7장 상수와 고급언어 165
    • 7.1 리터럴 상수와 프로그램 효율성 166
    • 7.2 리터럴 상수와 선언 상수 168
    • 7.3 상수식 169
    • 7.4 선언 상수와 읽기 전용 메모리 객체 171
    • 7.5 열거형 172
    • 7.6 불리언 상수 174
    • 7.7 실수 상수 176
    • 7.8 문자열 상수 182
    • 7.9 복합 데이터 타입 상수 186
    • 7.10 참고 자료 188
  • 8장 고급언어의 변수 189
    • 8.1 실행시 메모리 구성 189
      • 8.1.1 코드, 상수, 읽기 전용 섹션 190
      • 8.1.2 정적 변수 섹션 192
      • 8.1.3 BSS 섹션 193
      • 8.1.4 스택 섹션 195
      • 8.1.5 힙 섹션과 동적 메모리 할당 195
    • 8.2 변수란 무엇인가 196
      • 8.2.1 속성 196
      • 8.2.2 바인딩 196
      • 8.2.3 정적 객체 197
      • 8.2.4 동적 객체 197
      • 8.2.5 범위 197
      • 8.2.6 수명 198
      • 8.2.7 그래서 변수가 뭔데? 198
    • 8.3 변수 저장 공간 198
      • 8.3.1 정적 바인딩과 정적 변수 199
      • 8.3.2 유사 정적 바인딩과 자동 변수 203
      • 8.3.3 동적 바인딩과 동적 변수 206
    • 8.4 공통 기본 데이터 타입 210
      • 8.4.1 정수 변수 210
      • 8.4.2 부동 소수점/실수 변수 213
      • 8.4.3 문자 변수 214
      • 8.4.4 불리언 변수 215
    • 8.5 변수 주소와 고급언어 216
      • 8.5.1 전역 변수와 정적 변수의 저장 공간 할당 216
      • 8.5.2 자동 변수 사용을 통한 옵셋 크기 줄이기 217
      • 8.5.3 중간 변수를 위한 저장 공간 할당 223
      • 8.5.4 동적 변수와 포인터를 위한 저장 공간 할당 224
      • 8.5.5 레코드/구조체 사용을 통한 명령 옵셋 크기 줄이기 226
      • 8.5.6 레지스터 변수 227
    • 8.6 메모리에서 변수 정렬하기 229
      • 8.6.1 레코드와 정렬 235
    • 8.7 참고 자료 239
  • 9장 배열 자료형 241
    • 9.1 배열이란 무엇인가 242
      • 9.1.1 배열 선언 242
      • 9.1.2 메모리에서 배열 표현 246
      • 9.1.3 배열 원소에 접근하기 250
      • 9.1.4 패딩과 패킹 252
      • 9.1.5 다차원 배열 255
      • 9.1.6 동적 배열과 정적 배열 270
    • 9.2 참고 자료 279
  • 10장 문자열 자료형 281
    • 10.1 문자열 형식 282
      • 10.1.1 0으로 끝나는 문자열 282
      • 10.1.2 길이로 시작하는 문자열 300
      • 10.1.3 7비트 문자열 302
      • 10.1.4 HLA 문자열 303
      • 10.1.5 서술자 기반 문자열 306
    • 10.2 정적, 유사 동적, 동적 문자열 307
      • 10.2.1 정적 문자열 308
      • 10.2.2 유사 동적 문자열 308
      • 10.2.3 동적 문자열 309
    • 10.3 문자열의 참조 카운팅 309
    • 10.4 델파이/카이릭스 문자열 310
    • 10.5 고급 언어에서의 문자열 사용 311
    • 10.6 문자열의 문자 데이터 312
    • 10.7 참고 자료 314
  • 11장 포인터 자료형 315
    • 11.1 포인터의 정의와 오해 316
    • 11.2 고급언어에서의 포인터 구현 317
    • 11.3 포인터와 동적 메모리 할당 320
    • 11.4 포인터 동작과 포인터 연산 320
      • 11.4.1 포인터에 정수 더하기 321
      • 11.4.2 포인터에서 정수 빼기 323
      • 11.4.3 포인터에서 포인터 빼기 324
      • 11.4.4 포인터 비교 325
      • 11.4.5 논리 AND/OR와 포인터 327
      • 11.4.6 포인터의 다른 연산 328
    • 11.5 단순 메모리 할당자 예제 329
    • 11.6 가비지 콜렉션 331
    • 11.7 운영체제와 메모리 할당 332
    • 11.8 힙 메모리 오버헤드 333
    • 11.9 일반적인 포인터 문제 335
      • 11.9.1 포인터를 초기화하지 않은 채로 사용하는 것 335
      • 11.9.2 잘못된 값이 있는 포인터를 사용하는 것 337
      • 11.9.3 포인터를 해제한 후에 그 공간을 사용하는 것 337
      • 11.9.4 프로그램이 공간을 전부 사용한 후에 해제하지 않는 것 338
      • 11.9.5 잘못된 자료형으로 간접 참조하는 것 339
    • 11.10 참고 자료 340
  • 12장 레코드, 유니온, 클래스 자료형 341
    • 12.1 레코드 342
      • 12.1.1 여러 언어에서의 레코드 선언 342
      • 12.1.2 레코드의 인스턴스 생성 344
      • 12.1.3 컴파일시에 레코드 데이터 초기화 350
      • 12.1.4 레코드의 메모리 저장소 355
      • 12.1.5 메모리 효율을 높이기 위한 레코드 사용 358
      • 12.1.6 동적 레코드 타입과 데이터베이스 359
    • 12.2 판별자 유니온 360
    • 12.3 여러 언어에서의 유니온 선언 361
      • 12.3.1 C/C++의 유니온 선언 361
      • 12.3.2 파스칼/델파이/카이릭스의 유니온 선언 361
      • 12.3.3 HLA의 유니온 선언 362
    • 12.4 유니온의 메모리 저장소 363
    • 12.5 유니온의 또 다른 사용법 364
    • 12.6 가변형 365
    • 12.7 네임스페이스 369
    • 12.8 클래스와 객체 371
      • 12.8.1 클래스와 객체의 비교 371
      • 12.8.2 C++의 간단한 클래스 선언 372
      • 12.8.3 가상 메소드 테이블 373
      • 12.8.4 VMT의 공유 377
      • 12.8.5 클래스의 상속 377
      • 12.8.6 클래스의 다형성 380
      • 12.8.7 클래스, 객체, 성능 381
    • 12.9 참고 자료 382
  • 13장 산술 연산과 논리 연산 385
    • 13.1 산술식과 컴퓨터 구조 386
      • 13.1.1 스택 기반 컴퓨터 386
      • 13.1.2 누산기 기반 컴퓨터 391
      • 13.1.3 레지스터 기반 컴퓨터 393
      • 13.1.4 산술식의 대표적인 형태 394
      • 13.1.5 3주소 구조 394
      • 13.1.6 2주소 구조 395
      • 13.1.7 구조의 차이점과 코드 395
      • 13.1.8 복잡한 수식 다루기 396
    • 13.2 산술문 최적화 397
      • 13.2.1 상수 접기 397
      • 13.2.2 상수 전달 398
      • 13.2.3 죽은 코드 제거 400
      • 13.2.4 공통 부분식 제거 402
      • 13.2.5 연산 대체 406
      • 13.2.6 귀납 410
      • 13.2.7 루프 불변식 413
      • 13.2.8 최적화 도구와 프로그래머 416
    • 13.3 산술식에서의 부수효과 417
    • 13.4 부수효과 떼어내기: 시퀀스 포인트 421
    • 13.5 부수효과로 인한 문제점 회피 425
    • 13.6 계산 순서 강제 지정 425
    • 13.7 단축 연산 427
      • 13.7.1 단축 연산과 불리언 표현식 428
      • 13.7.2 단축 연산 혹은 완전 연산 강제하기 430
      • 13.7.3 효율성 문제 432
    • 13.8 산술 연산의 상대 비용 436
    • 13.9 참고 자료 437
  • 14장 분기 제어 구조 439
    • 14.1 제어 구조는 계산에 비해 느리다! 439
    • 14.2 로우레벨 제어 구조에 대한 소개 440
    • 14.3 goto문 443
    • 14.4 break, continue, next, return문 447
    • 14.5 if문 448
      • 14.5.1 특정 if/else문의 효율성 향상 450
      • 14.5.2 if문에서 완전 불리언 연산 강제하기 453
      • 14.5.3 if문에서 단축 불리언 연산 강제하기 460
    • 14.6 switch/case문 466
      • 14.6.1 switch/case문의 의미 467
      • 14.6.2 점프 테이블과 연속 비교 468
      • 14.6.3 switch/case문의 기타 구현 방법 475
      • 14.6.4 switch문에 대한 컴파일러의 출력물 487
    • 14.7 참고 자료 487
  • 15장 반복 제어 구조 489
    • 15.1 while 루프 489
      • 15.1.1 while 루프에서 완전 불리언 연산 강제하기 492
      • 15.1.2 while 루프에서 단축 불리언 연산 강제하기 501
    • 15.2 repeat..until(do..until/do..while) 루프 504
      • 15.2.1 repeat..until 루프에서 완전 불리언 연산 강제하기 507
      • 15.2.2 repeat..until 루프에서 단축 불리언 연산 강제하기 510
    • 15.3 forever..endfor 루프 515
      • 15.3.1 forever 루프에서 완전 불리언 연산 강제하기 518
      • 15.3.2 forever 루프에서 단축 불리언 연산 강제하기 518
    • 15.4 유한 루프(for 루프) 518
    • 15.5 참고 자료 520
  • 16장 함수와 프로시저 521
    • 16.1 간단한 함수와 프로시저 호출 521
      • 16.1.1 반환 주소 저장 525
      • 16.1.2 오버헤드의 다른 원인 529
    • 16.2 리프 함수와 리프 프로시저 530
    • 16.3 매크로와 인라인 함수 534
    • 16.4 함수나 프로시저로 인자 전달 540
    • 16.5 활성화 레코드와 스택 547
      • 16.5.1 활성화 레코드의 구성 550
      • 16.5.2 지역 변수에 옵셋 할당 553
      • 16.5.3 옵셋과 인자 555
      • 16.5.4 지역 변수와 인자에 접근 560
    • 16.6 인자 전달 방법 568
      • 16.6.1 값에 의한 전달 569
      • 16.6.2 참조에 의한 전달 569
    • 16.7 함수의 반환 값 571
    • 16.8 참고 자료 578
  • 부록 A 80x86 계열 CPU와 파워PC 계열 CPU의 간단한 비교 579
    • A.1 RISC와 CISC의 아키텍처 차이 580
      • A.1.1 명령어당 수행 작업 580
      • A.1.2 명령어의 크기 581
      • A.1.3 클록 속도와 명령어당 클록 수 581
      • A.1.4 메모리 접근과 주소 지정 방식 582
      • A.1.5 레지스터 583
      • A.1.6 즉시(상수) 오퍼랜드 583
      • A.1.7 스택 584
    • A.2 컴파일러와 애플리케이션 바이너리 인터페이스 문제 585

관련 블로그 글

『GREAT CODE 제2권』이 출간되었습니다!
GREAT CODE 시리즈의 제2권 로우레벨을 고려한 프로그램 최적화가 뽀사시한 모습으로 드디어 오늘 출간되었습니다.
이 책,『GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』는 고급언어로 작성된 소스 코드가 컴파일러를 거쳐 어떤식의 기계어 코드로 바뀌는지에 대해 집중적으로 다루고 있다. 우리가 흔히 사용하는 자료 구조나 상수, 변수가 실제로는 어떻게표현되는지, 연산이나 제어 구조가 기계어로는 어떻게 구현되는지, 컴파일러는 어떤 식으로 최적화를 수행하는지 심도 있게 다룸으로써코드상에서 나타나는 약간의 차이가 컴파일된 후의 기계어 코드에서는 얼마나 큰 차이가 되어 돌아오는지를 이해하기 쉽게 설명해준다.

저자도 강조하고 있지만 이 책의 목적은 어셈블리를 배워서 활용하자는 것이 아니다. 고급언어 코드가 어떻게 어셈블리혹은 기계어로 변환되는지를 이해하고, 이를 바탕으로 좀 더 효율적인 고급언어 코드를 작성할 수 있는 능력을 길러주는 책이다. 이책을 통해 내가 작성한 코드가 왜 느린지, 그리고 어떻게 해야 좀 더 좋은 코드를 만들어낼 수 있는지에 대해 확실하게 감을 잡을수 있을 것이다. 또한 컴퓨터 구조와 컴파일러 이론을 배운 사람들도 어렴풋하게 기억나는 내용을 깔끔하고 구체적으로 정리할 수있을 것이다. 독자 여러분이 하드웨어의 성능과 컴파일러의 기능을 충분히 활용하여 최고의 코드를 작성하는 데 이 책이 큰 도움이될 것으로 기대한다.
- 옮긴이의 말 중에서

지난 주 마감 작업 날은 대표역자셨던 이건호 님의 생일이었습니다. 자정을 딱 넘기는 순간 불을 끄고 축하 파티를 해드렸죠. 에이콘의 마감에는 즐거운 이벤트가 함께 합니다. 그간 회사일로도 많이 바쁘셨을 텐데 유학 준비하시랴 번역하시랴 수인님과 즐거운 번역여행 다니시느라 분주하셨을 건호님, 이제 다가오는 일요일이면 샌프란시스코로 떠나시는군요. 늘 건강하시구요. 열심히 공부하세요~! ^^/


CC

크리에이티브 커먼즈 라이센스 이 저작물은 크리에이티브 커먼즈 코리아 저작자표시 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.

기다리던『GREAT CODE 제2권』이 출간됩니다!
랜달 하이드 지음 | 이건호 안병규 박철현 임중근 심지웅 옮김 |
632페이지 | 7월 26일 출간 | 30,000원

에이콘 책들 중에 출간 일정 문의를 가장 많이 받았던 책이 무슨 책인지 아세요? 바로 『GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』였습니다. 이토록 독자 분들의 질문이 맡았던 이유는, 2005년 7월 출간한『GREAT CODE 제1권 하드웨어의 이해』 마지막 페이지에 2권 출간예정 광고를 실었던 때문이었기도 하겠지만, 무엇보다도 1권을 읽은 독자들에게 큰 감명(!)을 주었던 때문이 아니었을까 싶습니다. 후속편 격인 2권을 기대하게 만들었던 것이지요. 번역 기간 동안, 독자들께서 문의 메일을 주기적으로 보내주셔서 바로 역자분께 전달해드리면서 본의 아닌 원고 압박 효과를 톡톡히 누릴 수 있었다지요. :)

전자회로, 논리설계, 컴퓨터 아키텍처, 시스템 프로그래밍 등 4가지 필수 전공과목을 한 권에 일목요연하게 정리해준 『GREAT CODE 제1권 하드웨어의 이해』는 최고의 프로그래머가 되기 위해 반드시 알고 넘어가야 할 하드웨어와 컴퓨터 아키텍처에 대한 내용이 잘 정리되어 있습니다. 컴퓨터 관련 전공수업을 듣지 못했던 분, 배웠지만 실무에서 막상 쓰려고 하니 가물가물 기억나지 않는 분들을 위한 책이었습니다.

GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』는 C, C++, 자바 등 고급언어 프로그래머들을 위한 책으로서 하드웨어나 컴파일러, 기계어 등 로우레벨에 대한 이해를 통해 정말 효율적이고 최적화된 코드를 작성할 수 있는 능력을 키워줍니다.

태초에 대부분의소프트웨어는 어셈블리 즉, CPU의 로우레벨 언어로 작성되었다. 상대적으로느린 하드웨어에서 어느 정도의 성능을 끌어내기 위해서는 별다른 방법이 없었기 때문이다. 초기 프로그래머들은고급언어 컴파일러가 제대로 된 로우레벨 기계어 코드를 만들어내지 못한다는 것을 알고 있었기 때문에 고급언어로 코딩하는 것을 좋아하지 않았다. 그러나 오늘날에는 대부분의 프로그래머가 C, C++, 파스칼, 자바, 베이직 같은 고급언어를 사용하고 있다. 그 결과 비효율적이고 너절한 코드가 너무 많아지게 되었다. 그러나효율적인 소프트웨어를 만들기 위해 고급언어가 제공하는 생산성이나 이식성을 포기할 필요는 없다.

컴파일러가어떻게 동작하는지를 이해하면 아름다운 기계어 코드로 변환되는 소스 코드를 작성하는 것이 가능해진다. 바로지금 『Great Code 제2권 로우레벨을 고려한 프로그램최적화』를 읽으면 된다!     - 책 뒷표지에서


이 책은 원래 『GREAT CODE 제1권』의 저자인 랜달 하이드가 1권의 마지막 장에 넣으려고 했던 내용이라고 합니다. 로우레벨 수준을 고려해 광의의 최적화를 달성하는 최고의 코드를 만들기 위한 내용을 한 챕터에 담기에는 내용이 매우 방대해져서 별책으로 기획을 했다고 하죠.

번역을 맡으신 이건호님은 엔씨소프트에서 서버프로그래머로 일하다, 탁월한 평가를 받아 이달 말 UC 버클리에서 박사과정에 들어가게 되어 유학을 떠나게 되었습니다. 다시 한번 축하드리며 먼 길 떠나시는 건호님 앞길에 큰 축복이 함께 하길 바라겠습니다. :) 함께 공역하신 안병규님, 박철현님, 임중근님, 심지웅님께도 감사의 말씀을 전합니다.


GREAT CODE 제2권 로우레벨을 고려한 프로그램 최적화』의 출간을 기념하며, 『GREAT CODE 제1권 하드웨어의 이해』의 표지도 예쁘게 새단장을 했습니다. 형님이 아우따라 꽃단장을 한 셈이군요.
"GREAT CODE" 시리즈에 독자 여러분의 많은 성원을 바랍니다. 참고로 말씀드리면, 원서를 출간한 출판사에 문의한 결과 3권 출간에 대한 계획은 아직 잡혀지지 않았다고 합니다. 일정이 잡히게 되면 블로그에서 알려드리겠습니다.
CC

크리에이티브 커먼즈 라이센스 이 저작물은 크리에이티브 커먼즈 코리아 저작자표시 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.

도서 오류 신고

도서 오류 신고

에이콘출판사에 관심을 가져 주셔서 고맙습니다. 도서의 오탈자 정보를 알려주시면 다음 개정판 인쇄 시 반영하겠습니다.

오탈자 정보는 다음과 같이 입력해 주시면 됩니다.

(예시) p.100 아래에서 3행 : '몇일'동안 -> 며칠동안

정오표

[ p163 마지막 행 ]
작자의 지원 홈페이지(OLlyDbg → 작자의 지원 홈페이지(OllyDbg

[ p235 본문 2행 ]
ABI(애플리케이션 2진 인터페이스, → ABI(애플리케이션 이진 인터페이스,

[ p304 그림 10-4 ]
MaxLength → 최대 길이
Length → 길이

[ p310 그림 10-7 ]
Ref Count → 참조 카운터
Length → 길이

[ p320 두 번째 문단 7행 ]
C/C++와 같은 전통적인 언어는 프로그래머 명시적으로
→ C/C++와 같은 전통적인 언어는 명시적으로 프로그래머가

[ p330 아래에서 4-5행 ]
시스템의 큰 가용 블록의 수를 줄일 수 있다.
→ 시스템의 큰 가용 블록을 낭비할 수 있다.

[ p357 두 번째 코드 내 ]
padding1 :word;
     dField :dword;
  ↓
padding1 :word;

dField   :dword;

[ p359 6행 ]
별수를 접근할 경에 → 별수를 접근할 경우에

[ p363 코드 아래 1행 ]
n.i, n.u. n.r → n.i, n.u, n.r

[ p490 5행 ]
// While 루프: → // while 루프:

[ p584 본문 5행 ]
Call, ret, push, pop → call, ret, push, pop

[ p585 분문 1-2행 ]
애플리케이션 바이너리 인터페이스(ABI, Application Binari Interface)
→ ABI(애플리케이션 이진 인터페이스, Application Binary Interface)