0. 정리

  • 이론적인 현대 컴퓨터의 작동 원리는 다음과 같다.
    • 모든 컴퓨터는 메모리에 저장된 명령어를 순서대로 수행하는 방법으로 작동한다.
    • 메모리는 숫자 주소를 가지는 바이트 배열이며, 그 외에도 특수한 저장 장치인 레지스터가 존재한다.
    • 레지스터 중에는 다음에 수행할 명령어의 주소를 가리키는 레지스터(PC, 혹은 IR)가 존재한다.
    • 명령어는 데이터를 읽거나(fetch/load), 처리하거나, 쓰는(save/store) 과정이다.
    • 이러한 명령어는 실행 유닛에 의해 수행된다
    • 컴퓨터 역시 물리법칙을 준수하는 장치이므로, 명령어를 처리하거나 메모리를 읽거나 쓰는 데에는 그에 상응하는 시간 비용이 발생한다.
  • 그러나 실제 컴퓨터는 해당 원리에 의해 움직이는 것 처럼 보이게 (운영체제를 통해서) 거짓말을 하는 것이다. 실제 컴퓨터는 아래와 같은 한계를 숨기고 있다.
    • 메모리에 접근하는 데에는 명령어를 처리하는 것보다 훨씬 많은 시간이 필요하다.
    • 메모리는 바이트가 아니라 워드 단위로 접근한다. 따라서 워드 단위로 정렬되지 않은 데이터에 접근하는 데에는 더 많은 시간이 필요하다. 뿐만 아니라 구조체의 경우 자동으로 멤버 정렬을 수행하는 과정에서 사용되지 않는 바이트 ‘구멍’이 생길 수 있다.
    • 메모리마다 접근 속도가 다르다. L1/L2/L3 Cache와 Main Memory의 속도는 각 단계별로 10배 혹은 그 이상의 속도 차이가 존재한다.
    • 동일한 데이터도 프로세서에 따라 다르게 저장될 수 있다. 이때 이러한 저장 방식을 리틀 엔디언과 빅 엔디언으로 나누어 구분한다.
    • 메모리는 무한하지 않다.
    • 명령어 파이프라이닝이 항상 이상적인 속도로 작동하는 것은 아니다.
    • 컴퓨터는 일반적인 계산보다 분기 명령을 더 느리게 처리한다. 일반적으로 분기문은 지역성의 원리를 위배하기 때문이다.
    • 여러 프로그램은 실제로 한정된 하드웨어의 제어권을 놓고 경쟁한다.
    • 시스템 콜은 비용이 높다.
  • 또한 C++ 언어 자체에서도 다음과 같은 거짓말을 하고 있다.
    • 모든 명령문의 비용이 비슷하지 않다. 원시 타입의 대입문과 클래스 타입의 대입문이 차이가 있음을 생각해 보자.
    • 명령어는 반드시 작성된 순서대로 수행되지 않는다. 또한, 작성되었다고 반드시 수행되는 것도 아니다. 이러한 점이 동시성을 복잡하게 만든다.