바이너리의 단점

마지막 업데이트: 2022년 6월 14일 | 0개 댓글
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 트위터 공유하기
  • 카카오스토리 공유하기
[그림 3] 고수준·저수준 컨테이너 런타임 관계와 도커 아키텍처

흔들리는 도커(Docker)의 위상 - OCI와 CRI 중심으로 재편되는 컨테이너 생태계

IT 업계 종사자라면 컨테이너(Container)에 대해 한 번쯤은 들어본 적이 있을 것입니다. 애플리케이션과 바이너리, 라이브러리 등을 패키지로 묶어 배포하는 컨테이너는 서로 다른 컴퓨팅 환경에서 애플리케이션을 안정적으로 실행할 수 있으며 개발 환경에 구애 받지 않고 빠른 개발과 배포가 가능하다는 장점이 있습니다. 대표적인 IT 기업 중 하나인 구글은 지메일에서 유튜브, 검색에 이르기까지 모든 제품을 컨테이너에서 실행하고 있기도 합니다. 이처럼 컨테이너 기술은 IT 개발과 운영에 있어서 빼놓을 수 없는 필수 요소로 자리잡았습니다.

컨테이너에 대한 관심이 급격히 증가하면서 대부분의 주요 IT 벤더와 클라우드 공급자들은 컨테이너 기반의 솔루션을 발표했고 관련 스타트업 또한 급증해 컨테이너의 생태계를 넓혀왔습니다. 하지만 포맷과 런타임에 대한 특정한 규격이 없다 보니 컨테이너의 미래는 불안했던 것이 사실입니다. 일례로 2013년 출시된 도커(Docker)가 사실상의 컨테이너 표준 역할을 했지만 코어OS(CoreOS)는 도커와는 다른 규격으로 표준화를 추진하려 했습니다. 이러한 문제를 해결하기 위해 2015년 6월 도커, 코어OS, AWS, 구글, 마이크로소프트, IBM 등 주요 플랫폼 벤더들은 애플리케이션의 이식성(Portability) 관점에서 컨테이너 포맷과 런타임에 대한 개방형 업계 표준을 만들기 위해 OCI(Open Container Initiative)를 구성하였습니다. 이후 컨테이너 시장은 OCI의 런타임 명세와 이미지 명세를 준수하는 방향으로 성장하였고 그 과정에서 2016년 12월 쿠버네티스(Kubernetes)의 컨테이너 런타임을 만들기 위한 CRI(Container Runtime Interface)가 등장했습니다.

2. 컨테이너 런타임

CRI의 등장 배경을 이해하려면 먼저 컨테이너 런타임에 대해 살펴봐야 합니다. 컨테이너를 실행하기 위해서는 다음과 같은 세 단계를 거칩니다.

그림1의 1번 이미지 다운로드 2번 이미지를 번들로 압축해제 3번 번들에서 컨테이너를 실행

[그림 1] 컨테이너 실행 단계

OCI가 만들어질 당시 비공식적 표준 역할을 하던 도커는 컨테이너 런타임의 표준화를 위해 필요한 모든 단계가 아닌 세 번째 단계인 컨테이너의 실행 부분만 표준화하였습니다. 이로 인해 컨테이너의 런타임은 실제 컨테이너를 실행하는 저수준 컨테이너 런타임인 OCI 런타임과 컨테이너 이미지의 전송 및 관리, 이미지 압축 풀기 등을 실행하는 고수준 컨테이너 런타임으로 나뉘게 되었습니다.

저수준 컨테이너 런타임(Low-Level Container Runtimes)
컨테이너는 Linux namespace와 cgroup을 사용하여 구현합니다. namespace는 각 컨테이너에 대해 파일 시스템이나 네트워킹과 같은 시스템 리소스를 가상화하고 cgroup은 각 컨테이너가 사용할 수 있는 CPU 및 메모리와 같은 리소스 양을 제한하는 역할을 합니다. 저수준 컨테이너 런타임은 이러한 namespace와 cgroup을 설정한 다음 해당 namespace 및 cgroup 내에서 명령을 실행합니다.

그림 2는 도커와 runC의 관계도

[그림 2] 도커와 runC

OCI를 준수하는 저수준 컨테이너 런타임으로 가장 잘 알려진 것은 runC입니다. runC는 원래 도커에서 컨테이너를 실행하기 위해 개발되었으나, OCI 런타임 표준을 위해 독립적인 라이브러리로 사용되었습니다. 저수준 컨테이너 런타임은 컨테이너를 실제 실행하는 역할을 하지만 이미지로부터 컨테이너를 실행하려면 이미지와 관련된 API 같은 기능이 필요합니다. 이러한 기능은 고수준 컨테이너 런타임에서 제공됩니다.

고수준 컨테이너 런타임(High-Level Container Runtimes)
일반적으로 고수준 컨테이너 런타임은 원격 애플리케이션이 컨테이너를 논리적으로 실행하고 모니터링 하는데 사용할 수 있는 데몬 및 API를 제공합니다. 또한 컨테이너를 실행하기 위해 저수준 런타임 위에 배치됩니다.바이너리의 단점

이처럼 컨테이너를 실행하려면 저수준 및 고수준 컨테이너 런타임이 필요하기 때문에 OCI 런타임과 함께 도커가 그 역할을 했습니다. 도커는 docker-containerd라는 가장 잘 알려진 고수준 컨테이너 런타임을 제공합니다. containerd도 runC와 마찬가지로 도커에서 컨테이너를 실행하기 위해 개발되었으나 나중에 독립적인 라이브러리로 추출되었습니다.

그림3의 왼쪽 그림은 고수준 저수준 컨테이너 런타임 관계이며, 오른쪽 그림은 도커 아키텍쳐 그림

[그림 3] 고수준·저수준 컨테이너 런타임 관계와 도커 아키텍처

CRI(Container Runtime Interface)
CRI는 쿠버네티스에서 만든 컨테이너 런타임 인터페이스로 개발자들의 컨테이너 런타임 구축에 대한 진입 장벽을 낮추어 줍니다. 초기 쿠버네티스는 컨테이너를 실행하기 위해 도커를 사용하였는데 이는 쿠버네티스 클러스터 워커 노드의 에이전트인 Kubelet 소스코드 내부에 통합되어 있었습니다. 이처럼 통합된 프로세스는 Kubelet에 대한 깊은 이해를 필요로 하였고 쿠버네티스 커뮤니티에 상당한 유지보수 오버헤드를 발생시켰습니다. 이러한 문제를 해결하기 위해 쿠버네티스는 CRI를 만들어 명확하게 정의된 추상화 계층을 제공함으로써 개발자가 컨테이너 런타임 구축에 집중할 수 있게 하였습니다.

그림4는 Kubelet 동작의 흐름과 추상화 계층을 제공하는 CRI(Container Runtime Interface)의 구성도 I

[그림 4] Kubelet 동작 흐름과 CRI

3. 컨테이너의 새로운 생태계

CRI가 만들어진 후 주요 플랫폼 벤더들은 본격적으로 컨테이너 런타임 구축을 위해 노력하였습니다. 그 중 레드햇, 인텔, SUSE, Hyper, IBM 등의 관리자와 컨트리뷰터들이 커뮤니티 중심의 오픈소스 프로젝트인 CRI-O를 개발하였습니다.

CRI-O(Container Runtime Interface - Open Container Initiative)
CRI-O는 CRI와 OCI에서 유래된 프로젝트로 컨테이너 런타임 및 이미지가 OCI와 호환되는 것에 중점을 두고 있습니다. CRI 표준 컴포넌트를 최소한의 런타임으로 구현하며 쿠버네티스에서 모든 OCI 호환 런타임 및 컨테이너 이미지를 지원합니다.

그림 5는 Kubernetes의 Worker node로 도커와 CRI-O 두 가지 방법

[그림 5] 쿠버네티스와 도커 및 CRI-O

CRI-O는 컨테이너의 실행을 목적으로 경량화했기 때문에 도커가 제공하는 컨테이너 생성 및 이미지 빌드와 같은 기능은 제공하지 않습니다. 즉, CRI-O 덕분에 쿠버네티스는 컨테이너를 실행할 때 도커가 필요없었으나, 컨테이너의 생성 및 이미지 빌드와 같은 과정에서는 여전히 도커를 필요로 했습니다. 이러한 이유로 CRI-O 개발팀은 도커를 대체할 수 있는 새로운 생태계를 만들기 위해 노력하였습니다.

도커의 문제점
도커가 컨테이너의 생성 및 이미지 빌드를 모두 처리하는데 새로운 툴이 왜 필요할까요? 물론 기존 방식대로 도커를 사용할 수 있습니다. 그럼에도 CRI-O 개발팀이 도커의 역할을 대신할 수 있는 생태계를 위한 툴(Buildah 빌다, Podman 포드맨, Skopeo 스코피오)을 개발한 이유는 다음과 같은 문제점이 제기되었기 때문입니다.

도커는 클라이언트/서버 애플리케이션으로 클라이언트인 Docker CLI와 서버인 Docker daemon으로 구성됩니다. 그 중 서버는 컨테이너 이미지 빌드, 관리, 공유, 실행 및 컨테이너 인스턴스 관리와 같이 너무 많은 기능을 담당하는 데몬으로 모든 컨테이너를 자식 프로세스로 소유합니다. 이로 인해 무거울 뿐 아니라 장애가 발생하면 모든 자식 프로세스에 영향을 끼쳐 단일 실패점(Single point of failure)이 될 위험이 있습니다. 또한 클라이언트-서버 모델을 사용할 경우 리눅스의 audit.log를 통해 관리자가 시스템의 보안 이벤트를 감시하고 기록된 정보를 볼 수 있는 audit 보안 기능을 사용할 수 없게 됩니다.

그림 6은 fork·exec 모델 및 클라이언트-서버 모델에서의 UID, auid 설정 동작 방식이 있으며, 클라이언트 - 서버의 경우 컨테이너 이미지 빌드, 관리, 공유 같이 많은 기능이 이 있어 무겁고 장애 발생 시 모든 자식 프로세스에 영향을 미치게 된다는 것을 보여주는 그림 [그림 6] fork·exec 모델 및 클라이언트-서버 모델에서의 UID, auid 설정 동작 방식

추가로 모든 도커 명령은 루트 권한을 가진 사용자에 의해서만 실행할 수 있어 보안 문제가 발생할 수 있습니다. 이는 아래에 소개하는 Buildah, Podman, Skopeo를 사용하면 해결할 수 있습니다.

CRI-O와 함께 사용 가능한 툴: Buildah, Podman, Skopeo
Buildah, Podman, Skopeo는 별도의 데몬 없이 전통적인 fork·exec 모델을 사용하며 사용자 네임 스페이스를 이용해 컨테이너를 실행함으로써 단일실패점, audit 보안 기능 사용 및 루트 권한 문제를 해결하였습니다. 도커의 서버가 너무 많은 기능을 가지고 있는 단점은 각 툴 별로 다음과 같이 기능을 나누어 제공하는 방식으로 보완하였습니다.

그림 7은 Docker vs.Podnam의 그림으로 도커의 동작 흐름 및 Podman, Buidah, Skopeo의 역할

[그림 7] 도커의 동작 흐름 및 Podman, Buidah, Skopeo의 역할

Buildah는 CRI-O에서 이미지를 빌드할 때 도커의 종속성을 제거하기 위해 개발되었고 Dockerfile 없이 다른 스크립트 언어를 사용해 컨테이너 이미지를 빌드하는 것을 목표로 합니다.

Podman은 pull 및 tag 지정과 같은 OCI 컨테이너 이미지를 유지관리하고 수정하는데 도움이 되는 모든 명령 및 기능을 제공합니다. 또한 컨테이너 작성, 실행 및 유지보수도 할 수 있습니다. 즉, Docker CLI에서 수행할 수 있는 명령은 Podman CLI에서도 동일하게 수행 할 수 있습니다.

Buildah와 Podman은 일부 겹치는 기능이 있는데 Buildah는 OCI 이미지를 생성하는 효율적인 도구로, Podman은 그러한 이미지와 이미지를 통해 생성한 컨테이너를 유지하고 관리하는 도구로 이해하면 됩니다. 기술적으로 buildah run은 Dockerfile RUN을 에뮬레이트하며 podman run은 docker run을 에뮬레이트 합니다.

Skopeo는 이미지 저장소에서 다양한 작업을 수행하는 명령줄 도구입니다. 기존 도커가 다른 레지스트리에 이미지를 복사하기 위해 pull, tag, push를 사용했다면 Skopeo는 간단하게 copy 명령으로 해당 기능을 제공합니다. 추가로 저장소에 있는 이미지 이름에 대해 로우 레벨 정보를 제공해줍니다.

4. 컨테이너의 미래

지금까지 도커, OCI, CRI, Buildah, Podman 등 컨테이너 기술의 전반적인 흐름과 생태계에 대해 알아보았습니다. 컨테이너는 온프레미스 환경에서 클라우드 네이티브 환경으로 옮기는 것을 쉽게 해주기 때문에 클라우드 컴퓨팅 분야에서 가장 주목 받는 기술 중 하나로 성장하고 있습니다. 그리고 그 중심에는 단연 도커가 자리잡고 있습니다. 하지만 컨테이너의 표준화가 이뤄진 후 컨테이너 시장은 OCI와 CRI를 중심으로 성장하고 있습니다. 그 과정에서 사실상 컨테이너 표준으로 사용되던 도커의 역할을 대신하는 다양한 기술이 나오고 있으며 그 중 Buildah, Podman, Skopeo는 도커의 기능을 역할별로 나눠 구현하고 있습니다. 이들은 또한 도커의 보안 관련 단점을 보완하며 기존에는 없던 편리한 기능도 추가로 제공합니다.

컨테이너 생태계는 계속해서 성장하고 있으며 도커 외에도 사용할 수 있는 다양한 대체기술을 선보이고 있습니다. 향후 컨테이너는 OCI의 설립 목적인 ‘통일된 표준을 통해 어디서든 작동하는 이식성 제공’을 위해 OCI와 CRI 표준을 중심으로 생태계를 넓혀갈 것입니다.

▶ 해당 콘텐츠는 저작권법에 의하여 보호받는 저작물로 기고자에게 바이너리의 단점 저작권이 있습니다.
▶ 해당 콘텐츠는 사전 동의 없이 2차 가공 및 영리적인 이용을 금하고 있습니다.

GpgStudy 포럼

가만히 생각해보니 꼭 그렇지가 않은 것 같더라구요.
왜냐하면 STL이 템플릿 기반으로 작성된 것이고, 템플릿이라는 것이 일반적으로 코드를 작성하면 그 코드를 컴파일러가 컴파일 시점에 해석하여 각 데이터타입에 해당하는 코드로 만들어주는 것이니.

범용적으로 처리하기 위한 코드가 더 추가되는 것이 아니라
( 범용적으로 처리하기 위한 코드가 추가되는 단점이 있는 것이 아니라 )
다만 ( 컴파일러가 해당하는 데이터타입들에 대한 코드를 실제로 만들기 때문에 )
컴파일러가 신뢰성이 없다면 만들어지는 코드도 (바이너리도) 신뢰성이 없어진다

정리하자면 '컴파일러에 너무 의존적인 코드이다' 라는 것이 STL의 단점이 아닐까 라는 생각이 드는데, 이 생각이 과연 맞는걸까요?

지지 않으려는 마음은 가짜
이기고 싶은 마음이 진짜

전체글 글쓴이: 비회원 » 2008-07-15 17:01

풍류협객 작성: 흐흠. 제가 생각하고 있는 것을 말씀드려보자면
정리하자면 '컴파일러에 너무 의존적인 코드이다'( STL코드를 판단하는 컴파일러에 따라 안습인 코드도 나올 수 있다 ) 라는 것이 STL의 단점이 아닐까 라는 생각이 드는데, 이 생각이 과연 맞는걸까요?

이미 STL 은 표준화가 되어있는 녀석이고 따라서 표준 REF. 에 의해 반드시 이러이러한 것을 만족시켜야 한다라는 것이 어느정도 정의되어있습니다.

STL의 중요한 점 중 하나는 복잡도(Time complexity - 혹은 Big O 에 의한 알고리즘 복잡도)의 보장입니다.

따라서 어떤 컴파일러를 쓰던간에 표준을 만족하는 컴파일러라면, 반드시 STL의 특성이 그대로 구현 되어야 할 것입니다.

물론 특수한 케이스나 성능이 상수배 만큼 차이나는 경우는 있겠지요.

그러나 바이너리의 단점 STL의 정의나 표준을 살펴볼 때 STL 자체가 컴파일러에 의존적이다! 라고 말할 사항은 거의 없을 것으로 생각합니다.

물론 예전에 STL이 여기저기서 제대로 구현되기 시작할 초창기만 하더라도 특성을 타곤 했죠. ( VS 6.0 시절 SGI 헤더와 비교 하던 때도 있었죠.. )

여하튼 표준이 제정되어 그것이 제대로 산업에서 굳어지기 시작한 지금은 컴파일러 의존성이크다 라고 보기엔 문제가 있다고 생각합니다.

전체글 글쓴이: 풍류협객 » 2008-07-15 17:21

풍류협객 작성: 흐흠. 제가 생각하고 있는 것을 말씀드려보자면
정리하자면 '컴파일러에 너무 의존적인 코드이다'( STL코드를 판단하는 컴파일러에 따라 안습인 코드도 나올 수 있다 ) 라는 것이 STL의 단점이 아닐까 라는 생각이 드는데, 이 생각이 과연 맞는걸까요?

이미 STL 은 표준화가 되어있는 녀석이고 따라서 표준 REF. 에 의해 반드시 이러이러한 것을 만족시켜야 한다라는 것이 어느정도 정의되어있습니다.

STL의 중요한 점 중 하나는 복잡도(Time complexity - 혹은 Big O 에 의한 알고리즘 복잡도)의 보장입니다.

따라서 어떤 컴파일러를 쓰던간에 표준을 만족하는 컴파일러라면, 반드시 STL의 특성이 그대로 구현 되어야 할 것입니다.

물론 특수한 케이스나 성능이 상수배 만큼 차이나는 경우는 있겠지요.

그러나 STL의 정의나 표준을 살펴볼 때 STL 자체가 컴파일러에 의존적이다! 라고 말할 사항은 거의 없을 것으로 생각합니다.

물론 예전에 STL이 여기저기서 제대로 구현되기 시작할 초창기만 하더라도 특성을 타곤 했죠. ( VS 6.0 시절 SGI 헤더와 비교 하던 때도 있었죠.. )

여하튼 표준이 제정되어 그것이 제대로 산업에서 굳어지기 시작한 지금은 컴파일러 의존성이크다 라고 보기엔 문제가 있다고 생각합니다.

제가 설명을 잘 못 드린 듯 합니다.
제가 의도한 의미는 '컴파일러에 따라 코드의 성능이 달라질 수 있다' 라는 것이 아니라 다른 의미였습니다.
( 해당 부분의 구문은 수정하도록 하겠습니다 )

템플릿을 이용한 프로그래밍에서
데이터 타입에 따른 실제 코드는 컴파일러가 생성하는 것이므로

각 데이터 타입의 특성에 따라 하나 하나 직접 만든 자료구조나 알고리즘보다는
STL을 사용한 일반적 프로그래밍의 코드가 ( 컴파일러가 각 데이터 타입의 특성에 따라 그렇게 만들어주지 않는 이상 ) 성능이 떨어지는 것이 아니겠는가 ?

라는 . 그래서 컴파일러에 의존적인 코드가 아니겠는가 라는 의미였습니다.


다르게 얘기하자면
절차적으로 작성한 코드는 작성한 코드와 컴파일러가 생성한 바이너리의 코드가 일대일 대응인 반면
일반적으로 작성한 코드는 작성한 코드와 컴파일러가 생성한 바이너리의 코드가 일대다 이므로
아무래도 최적화 할 수 있는 여지가 없는 것이 아닌가 라는 얘기도 되겠네요.

지지 않으려는 마음은 가짜
이기고 싶은 마음이 진짜

시즌인가. stl은 일정 시기마다 한번씩 까이는 것 같네요;;

전체글 글쓴이: 그레이오거 » 2008-07-15 17:29

개인적으로는 stl 많이 씁니다. 주로 vector, deque, map, multimap, string이죠.
2005 쓰기 때문에 컴파일러 걱정도 크게 하지 않습니다. 그래서 boost같은 추가적인 확장 없이 걍 기본 그대로 씁니다.
critical section 문제나 추가기능이 필요한 녀석들은 간단한 wrapper를 만들어 씁니다.

단지 다들 아시는 몇가지만 주의합니다. 예를 들자면.

vector에서,
- reserve 제대로 안하고 실시간 push_back 했다간 피본다.
- clear가 clear가 아니다
- 실수로 범위 외 참조했다간 지옥을 볼 것이다

map에서,
- 집어넣을때는 최대한 insert(::value_type())을 애용한다
- 삽입/삭제가 심.바이너리의 단점 각.하.게 느리므로 로딩타임 외에는 감히 시도하지 않는다
(그래서 꼭 필요한 경우는 vector 두개를 사용해 맵 비스무리하게 만든 커스텀 자료형을 사용합니다)
- 인덱싱은 바이너리 서치로 이루어지므로 최대한 key로 문자열을 사용하는 만행은 피하자

머 stl의 장점은 잘 아실테고 몇몇 단점만 조심하면 충분히 신뢰할만하고, 안정적이며, 쓰기 편한데다, 적절히 빠르고, 무엇보다 팀원간 자료구조 문제로 다툴 일이 없어서 좋더군요.

하지만 이 몇몇 단점이 심하게 치명적이라 아무생각없이 쓰면 난리난다는데는 동의합니다.
반드시 케이스별로 프로파일링 해 보아야만 하며, 각 자료형의 특성을 만족시켜주어야만 합니다.
안그랬다 본 피가 산넘고 물건너 바다를 이룰 정도라. ㅠ_ㅠ

음. 아무래도 속도가 많이 떨어지죠.

전체글 글쓴이: sangduckx » 2008-07-15 17:57

물론 STL에 많은 종류들이 있지만.

MS에서 기본적으로 제공해주는 STL은 바이너리의 단점 성능이 몇십%가 정도가 아니고 특화된 것이랑 몇 천%씩 성능차이가 나는 경우가 흔하니까. -_-;;

Re: 시즌인가. stl은 일정 시기마다 한번씩 까이는 것 같네요;;

전체글 글쓴이: masque » 2008-07-15 18:08

전체글 글쓴이: 풍류협객 » 2008-07-15 18:15

그레이오거/ STL을 까려고 쓴 글이 아니라
잘 사용하려면, 단점도 확실히 알아야 하지 않을까 해서 질문 드린 것입니다.

모든 일에는 장점과 단점이 있을 테니까요

지지 않으려는 마음은 가짜
이기고 싶은 마음이 진짜

Re: 시즌인가. stl은 일정 시기마다 한번씩 까이는 것 같네요;;

전체글 글쓴이: mastercho » 2008-07-15 18:17

그레이오거 작성: 단지 다들 아시는 몇가지만 주의합니다. 예를 들자면.

vector에서,
- reserve 제대로 안하고 실시간 push_back 했다간 피본다.
- clear가 clear가 아니다
- 실수로 범위 외 참조했다간 지옥을 볼 것이다

map에서,
- 집어넣을때는 최대한 insert(::value_type())을 애용한다
- 삽입/삭제가 심.각.하.게 느리므로 로딩타임 외에는 감히 시도하지 않는다
(그래서 꼭 필요한 경우는 vector 두개를 사용해 맵 비스무리하게 만든 커스텀 자료형을 사용합니다)
- 인덱싱은 바이너리 서치로 이루어지므로 최대한 key로 문자열을 사용하는 만행은 피하자

vector 의 reserve 요거 정말 신중하게 안하면 피 볼수 있습니다
약 수년전 , 아무 생각없이 [솔직히 코딩이 귀찬아서. ] reserve 안하고 그냥 push_back 한적이 있습니다

문제는 극도로 자주 호출되는 함수 였고
[한 frame당 많게는 수백번까지]

그 함수내에서 push_back 이거 한 4-5번 하고 vector를 제거하는 코드가 들어 있었습니다
뭐 그당시 CPU를 30%이상 거의 넘게 쓰지 않다보니 , 뻔히 알고 있는 주의사항을 흘겨버렸었죠

결과는 충격적이었습니다
CPU가 수십%이상 바이너리의 단점 올라가더군요

아무리 대충썼거늘 이런 결과가 -_-; 나올까 하며 vector를 배열로 바꿨더니
cpu 증가가 1%도 안되더군요 .

그 후 vector에 자주 삽입을 하게 되는 코드를 실행할때는 reserve를 확인합니다
[물론 이것도 귀찬아서 reserve가 꼭 필요한 코드에만. ]
그러다가 fix_vector 같은 것을 연구해보기도 했습니다
우습게도 이런게 있을거 같은데 인터넷을 아무리 뒤져도 없더군요. 그래서 그냥 만들어봤습니다
[ viewtopic.php?t=13222&highlight=fix_vector ]

참, map의 삽입 삭제 오버헤드는 자료구조책의 균형 tree를 쪽을 보면 이해가 쉽지 않을까 합니다
검색속도의 일관성 및 향상을 위해 삽입 삭제시 , 각 노드마다 깊이 차이를 만들지 않기 위해 tree가
여러가지 일을 합니다
따라서 map를 제대로 쓰기 위해선 균형 tree에 관한 내용은 꼭 읽어보셔야 할거 같습니다
다만 그레이오거님의 "로딩타임 외에는 감히 시도하지 않는다"는 프로파일링을 통해 검증해야 할 사항이라
생각됩니다, 삽입 ,삭제가 충분할만한 조건이 그렇지 않을때보다 더 많지 않을까요?


또 map에 string을 넣지 말라는건 좀 의아하합니다
물론 정수형보다 느리다는건 자명하지만
string을 key 써야하는 일은 흔하게 많습니다
극도의 성능을 요구 하지 않는다면
그걸 정수형으로 key를 만들어내는게 더 안좋은 결과를 낼수 있지 않을까 하는데요
[한마디로 , 안좋은 형태로 데이타를 관리해야 하는 문제가 생기고요, 또한 스크립트 코드로 c++ 코드를 노출시키다보면 가장 완벽한 호환성을 가진것으로 string 만한게 없습니다 , 그러다보면 자주 string을 쓰게 되며
map에 string으로 키를 잡게 되는 경우가 -_-; 상당히 빈번하네요 사실
lua나 기타 스크립트에서 쓰는 container들이 다 맵구조로 되어 있지 않씁니까? ]

메모리에 올라와 있는 이런건 그렇게 까지 부담스럽지 않았던거 같았고
그마저 부담이 된다면 hash_map를 쓰면 무리가 없지 않을까 싶은데요

string을 비교하는 루틴을 보면 , 최악으로는 문자열 길이 만큼 비교횟수가 이루어지겠고, 최선은
일반 정수처럼 문자하나에서 바로 비교가 끝나기도 할겁니다

그런것이 고려된다면, 막연히 "최대한 key로 문자열을 사용하는 만행은 피하자"는 말씀은 오해의 여지가 있다고 생각됩니다

그리고 map 이나 set은 꼭 대형 자료구조에서만 쓰이는게 아니라
간단히 중복된 데이터를 넣지 않기 위한 용으로 간단하게 쓸 경우도 많고요

현재 프로젝트에 적절한가가 고려되어 선별적으로 쓰여야 할것이라 생각됩니다


ps
나머진 effective stl 에서 다룬 내용들이니 , 못보신분은 꼭 읽어보셔야할거 같습니다
저도 3번이상 읽었던거 같네요

바이너리의 단점

  • Node: 트리에서 데이터를 저장하는 기본 요소 (데이터와 다른 연결된 노드에 대한 Branch 정보 포함)
  • Root Node: 트리 맨 위에 있는 노드
  • Level: 바이너리의 단점 최상위 노드를 Level 0으로 하였을 때, 하위 Branch로 연결된 노드의 깊이를 나타냄
  • Parent Node: 어떤 노드의 다음 레벨에 연결된 노드
  • Child Node: 어떤 노드의 상위 레벨에 연결된 노드
  • Leaf Node (Terminal Node): Child Node가 하나도 없는 노드
  • Sibling (Brother Node): 동일한 Parent Node를 가진 노드
  • Depth: 트리에서 Node가 가질 수 있는 최대 Level

3. 이진 트리와 이진 탐색 트리 (Binary Search Tree)

  • 이진 트리: 노드의 최대 Branch가 2인 트리
  • 이진 탐색 트리 (Binary Search Tree, BST): 이진 트리에 다음과 같은 추가적인 조건이 있는 트리
    • 왼쪽 노드는 해당 노드보다 작은 값, 오른쪽 노드는 해당 노드보다 큰 값을 가지고 있음!

    4. 자료 구조 이진 탐색 트리의 장점과 주요 용도

    • 주요 용도: 데이터 검색(탐색)
    • 장점: 탐색 속도를 개선할 수 있음

    단점은 이진 탐색 트리 알고리즘 이해 후에 살펴보기로 함

    이진트리와 정렬된 배열간의 탐색 비교

    • 매우 복잡함. 경우를 나누어서 이해하는 것이 좋음

    5.4.1. Leaf Node 삭제

    5.4.2. Child Node 가 하나인 Node 삭제

    5.4.3. Child Node 가 두 개인 Node 삭제

    1. 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 삭제할 Node의 Parent Node가 가리키도록 한다.
    2. 삭제할 Node의 왼쪽 자식 중, 가장 큰 값을 삭제할 Node의 Parent Node가 가리키도록 한다.

    5.4.3.1. 삭제할 Node의 오른쪽 자식중, 가장 작은 값을 삭제할 Node의 Parent Node가 가리키게 할 경우

    • 삭제할 Node의 오른쪽 자식 선택
    • 오른쪽 자식의 가장 왼쪽에 있는 Node를 선택
    • 해당 Node를 삭제할 Node의 Parent Node의 왼쪽 Branch가 가리키게 함
    • 해당 Node의 왼쪽 Branch가 삭제할 Node의 왼쪽 Child Node를 가리키게 함
    • 해당 Node의 오른쪽 Branch가 삭제할 Node의 오른쪽 Child Node를 가리키게 함
    • 만약 해당 Node가 오른쪽 Child Node를 가지고 있었을 경우에는, 해당 Node의 본래 Parent Node의 왼쪽 Branch가 해당 오른쪽 Child Node를 가리키게 함

    5.5.2. Case1: 삭제할 Node가 Leaf Node인 경우

    5.5.2. Case2: 삭제할 Node가 Child Node를 한 개 가지고 있을 경우

    5.5.3. Case3-1: 삭제할 Node가 Child Node를 두 개 가지고 있을 경우 (삭제할 Node가 Parent Node 왼쪽에 있을 때)

    • 기본 사용 가능 전략
      1. 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 삭제할 Node의 Parent Node가 가리키도록 한다.
      2. 삭제할 Node의 왼쪽 자식 중, 가장 큰 값을 삭제할 Node의 Parent Node가 가리키도록 한다.
    • 기본 사용 가능 전략 중, 1번 전략을 사용하여 코드를 구현하기로 함
      • 경우의 수가 또다시 두가지가 있음
        • Case3-1-1: 삭제할 Node가 Parent Node의 왼쪽에 있고, 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 가진 Node의 Child Node가 없을 때
        • Case3-1-2: 삭제할 Node가 Parent Node의 왼쪽에 있고, 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 가진 Node의 오른쪽에 Child Node가 있을 때
          • 가장 작은 값을 가진 Node의 Child Node가 왼쪽에 있을 경우는 없음, 왜냐하면 왼쪽 Node가 있다는 것은 해당 Node보다 더 작은 값을 가진 Node가 있다는 뜻이기 때문임

          5.5.4. Case3-2: 삭제할 Node가 Child Node를 두 개 가지고 있을 경우 (삭제할 Node가 Parent Node 오른쪽에 있을 때)

          • 기본 사용 가능 전략
            1. 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 삭제할 Node의 Parent Node가 가리키도록 한다.
            2. 삭제할 Node의 왼쪽 자식 중, 가장 큰 값을 삭제할 Node의 Parent Node가 가리키도록 한다.
          • 기본 사용 가능 전략 중, 1번 전략을 사용하여 코드를 구현하기로 함
            • 경우의 수가 또다시 두가지가 있음
              • Case3-2-1: 삭제할 Node가 Parent Node의 오른쪽에 있고, 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 가진 Node의 Child Node가 없을 때
              • Case3-2-2: 삭제할 Node가 Parent Node의 오른쪽에 있고, 삭제할 Node의 오른쪽 자식 중, 가장 작은 값을 가진 Node의 오른쪽에 Child Node가 있을 때
                • 가장 작은 값을 가진 Node의 Child Node가 왼쪽에 있을 경우는 없음, 왜냐하면 왼쪽 Node가 있다는 것은 해당 Node보다 더 작은 값을 가진 Node가 있다는 뜻이기 때문임6. 이진 탐색 트리의 시간 복잡도와 단점

                • depth (트리의 높이) 를 h라고 표기한다면, O(h)
                • n개의 노드를 가진다면, ℎ = 𝑙 𝑜 𝑔 2 𝑛 h=log2n 에 가까우므로, 시간 복잡도는 𝑂 ( 𝑙 𝑜 𝑔 𝑛 ) O(logn)
                  • 참고: 빅오 표기법에서 𝑙 𝑜 𝑔 𝑛 logn 에서의 log의 밑은 10이 아니라, 2입니다.
                    • 한번 실행시마다, 50%의 실행할 수도 있는 명령을 제거한다는 의미. 즉 50%의 실행시간을 단축시킬 수 있다는 것을 의미함

                    15. 부동 소수점 산술: 문제점 및 한계¶

                    는 값 0/2 + 0/4 + 1/8을 가집니다. 이 두 소수는 같은 값을 가지며, 유일한 차이점은 첫 번째가 밑이 10인 분수 표기법으로 작성되었고 두 번째는 밑이 2라는 것입니다.

                    불행히도, 대부분의 십진 소수는 정확하게 이진 소수로 표현될 수 없습니다. 결과적으로, 일반적으로 입력하는 십진 부동 소수점 숫자가 실제로 기계에 저장될 때는 이진 부동 소수점 수로 근사 될 뿐입니다.

                    이 문제는 먼저 밑 10에서 따져보는 것이 이해하기 쉽습니다. 분수 1/3을 생각해봅시다. 이 값을 십진 소수로 근사할 수 있습니다:

                    등등. 아무리 많은 자릿수를 적어도 결과가 정확하게 1/3이 될 수 없지만, 점점 더 1/3에 가까운 근사치가 됩니다.

                    같은 방식으로, 아무리 많은 자릿수의 숫자를 사용해도, 십진수 0.1은 이진 소수로 정확하게 표현될 수 없습니다. 이진법에서, 1/10은 무한히 반복되는 소수입니다

                    유한 한 비트 수에서 멈추면, 근삿값을 얻게 됩니다. 오늘날 대부분 기계에서, float는 이진 분수로 근사 되는 데, 최상위 비트로부터 시작하는 53비트를 분자로 사용하고, 2의 거듭제곱 수를 분모로 사용합니다. 1/10의 경우, 이진 분수는 3602879701896397 / 2 ** 55 인데, 실제 값 1/10과 거의 같지만 정확히 같지는 않습니다.

                    많은 사용자는 값이 표시되는 방식 때문에 근사를 인식하지 못합니다. 파이썬은 기계에 저장된 이진 근삿값의 진짜 십진 값에 대한 십진 바이너리의 단점 근삿값을 인쇄할 뿐입니다. 대부분 기계에서, 만약 파이썬이 0.1로 저장된 이진 근삿값의 진짜 십진 값을 출력한다면 다음과 같이 표시해야 합니다

                    이것은 대부분 사람이 유용하다고 생각하는 것보다 많은 숫자이므로, 파이썬은 반올림된 값을 대신 표시하여 숫자를 다룰만하게 만듭니다

                    인쇄된 결과가 정확히 1/10인 것처럼 보여도, 실제 저장된 값은 가장 가까운 표현 가능한 이진 소수임을 기억하세요.

                    흥미롭게도, 가장 가까운 근사 이진 소수를 공유하는 여러 다른 십진수가 있습니다. 예를 들어, 0.1 과 0.10000000000000001 및 0.1000000000000000055511151231257827021181583404541015625 는 모두 3602879701896397 / 2 ** 55 로 근사 됩니다. 이 십진 값들이 모두 같은 근삿값을 공유하기 때문에 eval(repr(x)) == x 불변을 그대로 유지하면서 그중 하나를 표시할 수 있습니다.

                    역사적으로, 파이썬 프롬프트와 내장 repr() 함수는 유효 숫자 17개의 숫자인 0.10000000000000001 을 선택합니다. 파이썬 3.1부터, 이제 파이썬(대부분 시스템에서)이 가장 짧은 것을 선택할 수 있으며, 단순히 0.1 만 표시합니다.

                    이것이 이진 부동 소수점의 본질임에 주목하세요: 파이썬의 버그는 아니며, 여러분의 코드에 있는 버그도 아닙니다. 하드웨어의 부동 소수점 산술을 지원하는 모든 언어에서 같은 종류의 것을 볼 수 있습니다 (일부 언어는 기본적으로 혹은 모든 출력 모드에서 차이를 표시하지 않을 수 있지만).

                    좀 더 만족스러운 결과를 얻으려면, 문자열 포매팅을 사용하여 제한된 수의 유효 숫자를 생성할 수 있습니다:

                    이것이, 진정한 의미에서, 환영임을 깨닫는 것이 중요합니다: 여러분은 단순히 진짜 기곗값의 표시 를 반올림하고 있습니다.

                    하나의 환상은 다른 환상을 낳을 수 바이너리의 단점 있습니다. 예를 들어, 0.1은 정확히 1/10이 아니므로, 0.1의 세 개를 합한 것 역시 정확히 0.3이 아닙니다:

                    또한, 0.1은 1/10의 정확한 값에 더 가까워질 수 없고, 0.3도 3/10의 정확한 값에 더 가까워질 수 없으므로, round() 함수로 미리 반올림하는 것은 도움이 되지 않습니다:

                    숫자를 의도한 정확한 값에 더 가깝게 만들 바이너리의 단점 수는 없지만, round() 함수는 사후 반올림에 유용하여 부정확한 값을 가진 결과를 서로 비교할 수 있게 합니다:

                    Binary floating-point arithmetic holds many surprises like this. The problem with “0.1” is explained in precise detail below, in the “Representation Error” section. See The Perils of Floating Point for a more complete account of other common surprises.

                    끝이 가까이 오면 말하듯이, “쉬운 답은 없습니다.” 아직, 부동 소수점수를 지나치게 경계할 필요는 없습니다! 파이썬 float 연산의 에러는 부동 소수점 하드웨어에서 상속된 것이고, 대부분 기계에서는 연산당 2**53분의 1을 넘지 않는 규모입니다. 이것은 대부분 작업에서 필요한 수준 이상입니다. 하지만, 십진 산술이 아니며 모든 float 연산에 새로운 반올림 에러가 발생할 수 있다는 점을 명심해야 합니다.

                    병리학적 경우가 존재하지만, 무심히 부동 소수점 산술을 사용하는 대부분은, 단순히 최종 결과를 기대하는 자릿수로 반올림해서 표시하면 기대하는 결과를 보게 될 것입니다. 보통 str() 만으로도 충분하며, 더 세밀하게 제어하려면 포맷 문자열 문법 에서 str.format() 메서드의 포맷 지정자를 보세요.

                    정확한 십진 표현이 필요한 사용 사례의 경우, 회계 응용 프로그램 및 고정밀 응용 프로그램에 적합한 십진 산술을 구현하는 decimal 모듈을 사용해보세요.

                    정확한 산술의 또 다른 형태는 유리수를 기반으로 산술을 구현하는 fractions 모듈에 의해 지원됩니다 (따라서 1/3과 같은 숫자는 정확하게 나타낼 수 있습니다).

                    If you are a heavy user of floating point operations you should take a look at the NumPy package and many other packages for mathematical and statistical operations supplied by the SciPy project. See .

                    파이썬은 여러분이 float의 정확한 값을 진짜로 알아야 하는 드문 경우를 지원할 수 있는 도구들을 제공합니다. float.as_integer_ratio() 메서드는 float의 값을 분수로 표현합니다:

                    비율은 정확한 값이기 때문에, 원래 값을 손실 없이 다시 만드는 데 사용할 수 있습니다:

                    float.hex() 메서드는 float를 16진수(밑이 16이다)로 표현하는데, 컴퓨터에 저장된 정확한 값을 줍니다:

                    이 정확한 16진수 표현은 float 값을 정확하게 재구성하는 데 사용할 수 있습니다:

                    표현이 정확하므로, 파이썬의 다른 버전 에 걸쳐 값을 신뢰성 있게 이식하고 (플랫폼 독립성), 같은 형식을 지원하는 다른 언어(자바나 C99 같은)와 데이터를 교환하는 데 유용합니다.

                    또 다른 유용한 도구는 math.fsum() 함수입니다. 이 함수는 합산 동안 정밀도 상실을 완화합니다. 누적 합계에 값이 더해지면서 “잃어버린 숫자들”을 추적합니다. 최종 합계에 영향을 주는 지점까지 에러가 누적되지 않아서 전체적인 정확도에 차이를 만들 수 있습니다:

                    15.1. 표현 오류¶

                    이 섹션에서는 “0.1” 예제를 자세히 설명하고, 이러한 사례에 대한 정확한 분석을 여러분이 직접 수행하는 방법을 보여줍니다. 이진 부동 소수점 표현에 대한 기본 지식이 있다고 가정합니다.

                    표현 오류 (Representation error) 는 일부 (실제로는, 대부분의) 십진 소수가 이진(밑 2) 소수로 정확하게 표현될 수 없다는 사실을 나타냅니다. 이것이 파이썬(또는 펄, C, C++, 자바, 포트란 및 기타 여러 언어)이 종종 여러분이 기대하는 정확한 십진수를 표시하지 않는 주된 이유입니다.

                    왜 그럴까? 1/10은 이진 소수로 정확히 표현할 수 없습니다. 오늘날 (2000년 11월) 거의 모든 기계는 IEEE-754 부동 소수점 산술을 사용하고, 거의 모든 플랫폼은 파이썬 float를 IEEE-754 “배정밀도”에 매핑합니다. 754 배정밀도는 53비트의 정밀도가 포함되어 있어서, 입력 시 컴퓨터는 0.1을 J/2**N 형태의 가장 가까운 분수로 변환하려고 노력합니다. 여기서 J 는 정확히 53비트를 포함하는 정수입니다.:

                    로 다시 쓰고, J 가 정확히 53 비트( >= 2**52 이지만 < 2**53 입니다)임을 고려하면, N 의 최적값은 56입니다:

                    즉, 56은 J 가 정확히 53비트가 되도록 만드는 N 의 유일한 값입니다. J 의 가능한 값 중 가장 좋은 것은 반올림한 몫입니다:

                    나머지가 10의 절반보다 크므로, 가장 가까운 근삿값은 올림 해서 얻어집니다:

                    따라서 754 배정밀도로 1/10 에 가장 가까운 근삿값은 다음과 같습니다:

                    분자와 분모를 둘로 나누면 다음과 같이 약분됩니다:

                    올림을 했기 때문에, 이것은 실제로 1/10 보다 약간 크다는 것에 유의하세요; 내림을 했다면, 몫이 1/10 보다 약간 작아졌을 것입니다. 그러나 어떤 경우에도 정확하게 1/10일 수는 없습니다!

                    따라서 컴퓨터는 결코 1/10을 “보지” 못합니다: 볼 수 있는 것은 위에서 주어진 정확한 분수, 얻을 수 있는 최선의 754 배정밀도 근삿값입니다:

                    그 분수에 10**55를 곱하면, 55개의 십진 숫자를 볼 수 있습니다.:

                    이는 컴퓨터에 저장된 정확한 숫자가 십진수 0.1000000000000000055511151231257827021181583404541015625와 같음을 의미합니다. 전체 십진법 값을 표시하는 대신, 많은 언어(이전 버전의 파이썬 포함)는 결과를 17개의 유효숫자로 반올림합니다:

                    바이너리의 단점

                    안녕하세요~ 오늘은 BCD코드에 대해서 포스팅을 해보려고 합니다.

                    2. 왜 8421코드란 이름이 붙었는가?

                    BCD Code란?

                    BCD는 'Binary Coded Decimal'의 약자로 이걸 해석하면 '이진화 십진법'이예요. 직독직해하면 십진수처럼 코딩된 이진수, 말이 좀 어렵죠? BCD코드는 8421코드라는 다른 말도 가지고 있습니다.

                    여러분들 알다시피, 컴퓨터는 0 또는 1, 요 두 글자만 사용해서 모든걸 표현해요.

                    우리는 10진수를 써서 수를 표현한다면, 컴퓨터의 세계는 2진수인거죠.

                    그런데 이 2진수는 사람이 보기 매우매우 불편합니다. 그래서 사람이 보는 화면에는 십진수로 보여주고, 내부적으로 이 10진수 값을 2진수로 바꿔서 사용해야해요. 반대로도 마찬가지! 이진수로 컴퓨터에서 투닥투닥 필요한 연산을 수행한 다음에, 결과를 다시 십진수로 변환해 사람에게 보여주는 프로세스를 거쳐야해요.

                    그럼 우리가 사용하고 있는 10진수를 어떻게 2진수로 표현할까? 다양한 방법론이 있는데 그 중 하나가 BCD코드입니다.

                    현재 데이터 통신에는 ASCII코드가 가장 많이 사용되고 있었으나, 컴퓨터 초창기때에는 BCD코드를 사용했었어요.

                    그렇다고 지금 안쓰나?? 그렇지 않습니다. BCD만의 특별한 이점들 때문에 현재도 종종 필요에 따라 사용됩니다. (이는 아래에서 살펴보도록 해요) 또 초창기에는 숫자에 관련된 부분만 고안되었지만(?) 추후 문자등 더 많은 데이터를 표현할 수 있도록 여러 방법으로 확장되었습니다.

                    BCD변환법

                    BCD코드는 10진수 자리수마다 1=1매칭해서 2진수로 변환하는걸말해요. 따라서 BCD코드는 10개의 코드만 갖고 있습니다.(0부터 9까지!) 따라서 필요한 bit는 4bit입니다.

                    2진법과 BCD코드의 두드러진 차이점은 10부터 확 차이가 나요.

                    [8bit 기준 바이너리코드와 BCD코드 비교]

                    10진수 Binary BCD코드
                    0 0000 0000 0000 0000
                    1 0000 0001 0000 0001
                    2 0000 0010 0000 0010
                    3 0000 0011 0000 0011
                    4 0000 0100 0000 0100
                    5 0000 0101 0000 0101
                    6 0000 0110 0000 0110
                    7 0000 0111 0000 0111
                    8 0000 1000 0000 1000
                    9 0000 1001 0000 1001
                    10 0000 1010 0001 0000 바이너리의 단점
                    11 0000 1011 0001 0001
                    12 0000 1100 0001 0010
                    13 0000 1101 0001 0011

                    BCD는 하나의 자리수를 표현하는데 4bit를 쓰죠. 근데 4bit로 만들 수 있는 가짓수는 총 16개예요.

                    십진수 기수는 총 10개이므로 10부터 15에 해당하는 남은 6개의 표현값은 BCD에서 쓰이지 않습니다.

                    [4bit만으로 표현할 수 있는 코드]

                    4bits BCD코드
                    0000 0을 표현하는데 사용
                    0001 1을 표현하는데 사용
                    0010 2을 표현하는데 사용
                    0011 3을 표현하는데 사용
                    0100 4을 표현하는데 사용
                    0101 5을 표현하는데 사용
                    0110 6을 표현하는데 사용
                    0111 7을 표현하는데 사용
                    1000 8을 표현하는데 사용
                    1001 9을 표현하는데 사용
                    1010 사용안함
                    1011 사용안함
                    1100 사용안함
                    1101 사용안함
                    1110 사용안함
                    1111 사용안함

                    8421코드?

                    BCD코드를 구성하는 수는 그 위치에 따라서 가중치를 가집니다. 각 자리수마다 8,4,2,1가중치를 갖기 때문에 BCD코드는 8421코드라고도 불려요.

                    0011의 BCD코드를 10진수로 변환하려면 어떻게 해야 하나? 각 자리수가 8421가중치를 가지고 있으므로 1이 있는데만 가중치를 더해주면 됩니다.

                    BCD를 10진수로 변환

                    왜 굳이 8421코드라 불르나, 다른 가중치 방법이 나왔으니까 이런 이름이 붙었겠죠!? 비교를 위해서죠 뭐 (ex 5421코드 등)

                    https://www.tutorialspoint.com/computer_logical_organization/binary_codes.htm

                    암튼 8421코드는 BCD코드의 다른 이름이라는거 ~

                    BCD코드 가산법

                    바이너리코드가 아닌 BCD코드를 사용하게 되면 2진 가산법을 그대로 적용하지 못하는 문제점이 있어요. 이게 무엇인지 보고 어떻게 해결했는지 확인해봅시다. 즉! 이진법처럼 계산하시면 안돼요! 13+7을 예시로 볼게요.

                    출처: http://blog.naver.com/PostView.nhn?blogId=cni1577&logNo=221580366043&parentCategoryNo=&categoryNo=35&viewDate=&isShowPopularPosts=true&from=search

                    왼쪽은 10진수 13+7 계산법이고

                    오른쪽은 이를 BCD코드로 구현한 뒤 이진법처럼 계산한 모습입니다.

                    그런데 BCD코드에 1010이 있었나요. ㄴㄴ , [4bit만으로 표현할 수 있는 코드]부분 올라가서 보고 오시면 1010은 사용하지 않아요.

                    그럼 BCD코드를 할 때 어떻게 해야할까요?

                    9를 넘어선 값이 나오면 6에 해당하는 0110을 더해줘야 해요. [왼쪽 그림 참고]

                    왜 6을 더해줄까요?? 그건 이진수 특징인 4bits가 16가짓수이고, 10진수는 10개의 값만 가지기 때문에 carry를 넘기기 위해서는 6을 더해서 순환시켜줘야 하기 때문이예요.

                    0110(2)을 더해줘야 하는 원리 자세히 설명하기

                    [십진수개념에서 생각해보자: 3+7=10인 이유 분석해보기]

                    자, 3+7을 계산하면 캐리 1이 발생하고 나머지가 0이 남습니다. 그래서 3+7=10이 되는거예요. 즉 10은 캐리와 나머지의 조합이다.

                    보면 캐리가 발생하면 다음 기수로 넘어가게 돼요. 왜냐!! 10진법은 표현할 수 있는 범위가 0~9까지이기 때문 즉, 하나의 기수에 10을 표현할 수 없기 때문이예요.

                    [BCD개념에서 생각해보자]

                    8421코드도 마찬가지입니다. 이진수이긴 하지만 10진수처럼 코딩되어 있다고 했죠?? 얘도 표현할 수 있는게 0~9까지라 했어요.

                    자 더했더니 10 이 됐어요. 그럼 기수를 하나 넘기고 0이 되어야겠죠. 근데 이진수처럼 계산했더니 값이 1010이 나왔어요. 얘는 BCD코드에서 사용할 수 없죠. 표현할 수 있는 기수가 아닙니다. 따라서 0000으로 초기화되고 캐리로 1을 넘겨줘야겠죠

                    (값이 10이므로 딱 떨어져 나머지가 0이므로)

                    BCD코드의 처음인 0000이 됐어요. 이게 다시 처음으로 돌아가려면 몇을 더해줘야 할까요? 즉 순환하려면 어떻게 계산되어야할까요? 4bit의 특징을 생각해봅시다. 0000-> 0001-> 0010 -> 0011..등의 16개 가짓수를 다 돌아야 다시 0000부터 반복되겠죠.(순서대로 1씩 더해보세요 1111 +1 이 되어야 1+0000이 되어 뒤의 4bit가 다시 0000이 되겠죠?) 우리가 16가짓수 중10개만 사용하니까 다시 되돌아가라면 6을 더해줘야 순환이 됩니다. 따라서 사용하지 않는 값이 나오면 6을 더해주어야 제대로 된 계산이 되는거예요. (잘 설명을 했는지 모르겠네요 ㅎㅎ ㅋㅋ)

                    연습문제를 보는게 계산하는 법을 이해하는게 더 빠를수도!

                    17+46 출처: http://blog.naver.com/PostView.nhn?blogId=cni1577&logNo=221580366043&parentCategoryNo=&categoryNo=35&viewDate=&isShowPopularPosts=true&from=search

                    BCD코드 특징

                    바이너리코드와 BCD코드의 차이점을 명확히 보기 위해 예시를 들어봅시다.

                    방법 표현 크기
                    BCD코드 변환시 0001 1000 0101 12bit
                    바이너리코드 변환시 1011 1001 8bit

                    장점1: 같은 수 체계로 유저에게 친숙

                    자 표현만 봤을 때 바이너리코드(1011 1001)의 경우 해당 값이 185라는걸 한번에 알 수 있겠나요??

                    계산을 일일이 해봐야 알 수 있어요. 10진수처럼 표현되기 때문에 사람들에게 더 친숙합니다.

                    장점2: 하드웨어적 구현이 굳

                    그리고 하드웨어 알고리즘 적으로 BCD변환기는 다른 알고리즘에 비해 구현하기 심플합니다)

                    자리수에 해당하는 변환기하나만 있으면 되기 때문에 사람들에게 입력을 받고 또 이를 보여주는 DISPLAY하는 데에 효율적입니다.

                    장점3: 따라서 사용자에게 입력받고 보여줄때 효율적

                    컴퓨터에서는 대게의 사용자가 BCD코드 형태로 데이터를 입력하며 이 코드가 컴퓨터 내부에서 2진수로 변환되어 저장과 연산이 시행 되도록 도와줘요. BCD 표현 방식의 장점으로는 수의 크기에 제한없이 자릿 수가 증가 할 마다. 새로 4비트만 더 부가 하면 된다는거.

                    장점4: ASCII보다 차지하는 크기가 작음.

                    단점1: 바이너리보다 차지하는 크기가 큼

                    또 크기를 보면 바이너리는 8bits인데 BCD는 12bits로 더 많은 bit를 사용합니다. 따라서 바이너리보다 high speed를 내기 어려워요.

                    하지만 현재 가장 많이 사용되는 ASCII코드는 1문자당, 8bits나 차지해요. 즉 185면 8*3해서 24bits! 근데 BCD는 한 문자당 4bits만 차지하니까 ASCII보다 반이나 줄게 됩니다. (1/2배)

                    이 특징 때문에, 현재 데이터 전송시에, 많은 데이터를 함축적으로 보내려고 ASCII -> BCD변환을 사용하기도 합니다.

                    장점5: 정확한 소숫점 표현

                    10진수 0.2를 binary code로 표현해봅시다.

                    그러면 0.001100110011. 의 무한한 소수값을 갖게 돼요.

                    그런데 이를 BCD코드로 표현하면. 0.0010으로 매우 심플하게 표현됩니다.

                    BCD는 부동소수점에 이점이 있기 때문에 과학적이거나 그래픽적인 계산기에서 여전히 사용됩니다.

                    모든 HP계산기는 BCD를 사용한다고 하네요. (2016년 검색 기준)

                    출처: https://www.quora.com/How-much-is-Binary-Coded-Decimal-BCD-used-these-days-and-how-important-is-it-where-it-is-used


0 개 댓글

답장을 남겨주세요