728x90


단편화는 기억장치(RAM, HDD 등등)의 빈 공간 또는 자료가 여러 개의 조각으로 나뉘는 현상을 말한다.

단편화 현상은 기억장치의 사용 가능한 공간을 줄이거나, 읽기와 쓰기의 수행 속도를 늦추는 문제점을 일으킨다.


메모리 단편화는 시스템에서 사용할 수 있는 메모리를 할당 받을 수 없거나 원치않는 메모리 낭비가 발생하는 현상이다.

메모리 할당자(allocator)가 해당 메모리를 할당 할 수 있는 상태로 만들 수 없을 때 생겨나며, 

이들이 불연속적인 작은 부분에 따로 흩어져 있기 때문에 메모리 단편화 문제가 발생한다.


내부 단편화는 할당을 요청한 크기에 비해서 실제 할당된 메모리의 크기가 커서, 해당 메모리 공간 내에서 사용하지 않는 부분이 있을 때 발생한다.

예) 패딩비트

struct
{

    char a;         // 1 padding byte after a

    short b;

    char c[5];      // 3 padding bytes after c

    double d;

    char e[2];      // 2 padding bytes added at the end

};



외부 단편화는 할당 가능한 메모리 크기의 Heap에 비해 실제 할당 가능한 개별 메모리 공간의 크기가 매우 작은 상태를 의미한다.


출처 : http://leehayun123.tistory.com/20

728x90
728x90

가상 메모리
는 프로세서가 운영체제와 조합되어 응용 소프트웨어와 물리 메모리 사이에 가상의 계층을 만들고 하드웨어의 일부분을 메모리 처럼 사용하는 것으로 가상 메모리 관리 기법에는 페이징과 세그멘테이션이 있다.



페이징 기법은 가상 메모리를 모두 같은 크기의 블록으로 나누어 프로세스를 메인 메모리에 올려 동작하게 하는 기법으로 이때 일정한 크기로 나누어진 블록을 페이지(프레임의 시작 주소)라고 하며 메인 메모리는 프레임이라는 단위로 나뉘고 페이지와 크기는 같다.

또한 페이징 기법은 일정한 크기로 페이지를 나누기 때문에 내부 단편화가 발생할 수 있다.


페이지 테이블은 프로세스의 페이지 정보(프레임의 시작 주소)를 저장하고 각 프로세스의 메모리가 실제 메모리와 어떻게 맵핑 되어 있는지를 나타내는 테이블로 하나의 프로세스는 하나의 페이지 테이블을 가진다. 페이지 테이블은 페이지 번호를 가지고 있으므로 페이지를 찾아간 후 페이지의 프레임 시작 주소를 통해 메인 메모리의 주소를 알 수 있다.


세그멘테이션은 가상 메모리를 서로 크기가 다른 논리적 단위인 세그먼트(Segment)로 메모리를 할당하는 기법이다.

미리 분할해 둘 수 없고 메모리에 적재 될 때 빈 공간을 찾아 할당하는 사용자 관점의 가상 메모리 관리로써 세그먼트의 가변적 크기로 인해 외부 단편화가 일어난다.


메모리 풀은 동일한 사이즈의 메모리 블록들을 미리 할당해 놓음으로써 다양한 블록 사이즈 때문에 생기는 단편화와 그로 인한 퍼포먼스 저하를 방지하기 위해 사용되는 기법이다.


메모리 압축은 여러 가지 크기의 사용되지 않는 메모리들을 가능하면 연속된 큰 메모리 블록이 되도록 하나로 통합하는 과정이다.



출처 : http://leehayun123.tistory.com/20






728x90
728x90

DirectCompute에는 다양한 메모리 모형이 존재한다. 이것들을 한번 살펴보자.


레지스터 기반 메모리

계산 셰이더는 다른 프로그래밍 가능 셰이더 단계들과 동일한 하드웨어(프로그래밍이 가능한 처리기)에서 실행된다. 따라서 계산 셰이더 단계에서도 다른 프로그램 가능 셰이더 단계들에서와 동일한 일반적인 처리 패러다임이 적용되며, 공통 셰이더 코어를 구현한다. 계산 셰이더 단계는 다른 파이프라인 단계들과 비슷한 레지스터 기반 처리 개념을 사용한다.

계산 셰이더는 입력 특성 레지스터들(v#)과 텍스처 레지스터들(t#), 상수 버퍼 레지스터들(cb#), 순서 없는 접근 레지스터들(u#), 임시 레지스터들(r#, x#)을 지원한다.


임시 레지스터들은 셰이더 프로그램의 실행 도중 중간 계산 결과를 담는 데 주로 쓰인다.

임시 레지스터는 현재 실행 중인 스레드에서만 접근할 수 있으며, 일반적으로 접근이 극도로 빠르다.

공통 셰이더 코어에는 임시 레지스터를 최대 4096개까지(r#, x# 합쳐서) 지정할 수 있다.

일반적으로 셰이더 코어에 적재된 자료는 최대한 이 임시 레지스터들에 저장된다.


장치 메모리

레지스터 기반 메모리는 아주 빠르긴 하지만 용량이 매우 작다. 또한, 사용할 자료를 먼저 셰이더 코어에 적재한 후에야 레지스터들을 사용할 수 있으며, 스레드의 셰이더 프로그램 실행이 끝나면 레지스터 내용이 초기화된다. 계산 파이프라인을 본격적으로 활용하려면 레지스터 기반 메모리에 담을 수 있는 것보다 훨씬 큰 자원이 필요하며, 또한 셰이더 프로그램의 실행들 사이에서 계속 유지되는 자원도 필요하다. 그런 자원들은 장치 메모리에 담는다. 그리고 그런 자원들을 다른 종류의 메모리에 저장되는 자원과 구분해서 흔히 장치 메모리 자원이라고 부른다. 

728x90
728x90

DirectCompute의 스레드 적용 모형

DirectCompute에 관해 가장 먼저 살펴볼 것은 그 스레드 적용 모형과 실행 모형이다. GPU가 많은 수의 처리 코어들로 이루어져 있기 때문에 병렬 알고리즘 처리에 아주 적합하다는 점은 앞에서 이미 언급했다. 작업을 수행할 처리기가 아주 많기 때문에, 특정 알고리즘을 다수의 스레드들에 효과적으로 대응시키기 위한 적당한 방법론을 갖출 필요가 있다. 

전통적인 CPU 기반 알고리즘에 쓰이는 전형적인 다중스레드는 스레드들이 각자 개별적으로 실행되되 공유 메모리 공간과 수동적인 동기화를 통해서 서로 의사소통하는 방법을 사용한다. 이는 다중 프로세서 시스템들에서 오랫동안 쓰였다.

그러나 이러한 모형은 수천 개의 스레드가 동시에 돌아가야 하는 처리 패러다임에 그리 잘 맞지 않는다. DirectCompute는 일반성과 사용 편의의 균형을 도모하는 또 다른 종류의 스레드 적용 및 실행 모형을 사용한다. 이 모형에서는 스레드들을 자료 요소에 사상(대응)시키기가 더 쉽다. 이 덕분에 하나의 처리 과제를 더 작은 여러 조각으로 분할해서 GPU에서 실행하는 것이 수월해진다.


핵(Kernel) 기반 처리

계산 셰이더 단계는 핵(Kernel) 기반 처리 시스템을 구현한다. 계산 셰이더 프로그램 자체는 하나의 함수이며, 그 함수는 일종의 핵함수(Kernel Function)처럼 작용한다. 작업의 단위는 알고리즘마다 다르지만, 현재 적재된 핵의 각 인스턴스가 각각의 단일한 입력 자료 집합을 처리하는 식으로 알고리즘이 실행된다는 점은 동일하다. 계산 셰이더에 주어지는 입력자료는 계산 셰이더 단계에 연결된 자원에서 온 것이다. 이러한 구조에서는 하나의 알고리즘이 GPU상의 수천 개의 스레드들에서 실행되도록 프로그래밍하기가 아주 쉽고 직관적이다. 모든 스레드가 같은 핵(Kernel)을 실행하므로, 스레드들의 행동을 일일이 동기화하는 데 힘을 빼는 대신 원하는 자료를 각자 독립적으로 처리될 자료 항목들로 분할하는 방법을 찾는 데 주력하면 된다.


작업의 배분

작업을 시작할 때 사용할 수 있는 메서드는 ID3D11DeviceContext::Dispatch() 또는 ID3D11DeviceContext::DispatchIndirect()이다.

이 함수들은 렌더링 파이프라인의 여러 그리기 메서드(Draw로 시작하는 함수)들에 비견할 수 있다. 

Dispatch메서드는 부호 없는 정수 매개변수 3개를 받는데 이 세 매개변수는 원하는 처리 핵의 실행을 '배분(dispatch)' 할 스레드들의 그룹의 개수를 결정한다. 좀 더 구체적으로 이 세 매개변수는 인스턴스화할 스레드 그룹들을 담는 3차원 배열의 x, y, z 차원 크기로, 유효한 범위는 셋 모두 1에서 65535이다. 예를 들어 응용 프로그램이 Dispatch(4, 6, 2)를 호출했다면 4 x 6 x 2 = 48개의 스레드 그룹들이 만들어진다. 그 배열 안에서 각 스레드 그룹은 세 차원 색인(범위는 0 ~ 차원 크기 - 1)의  고유한 조합으로 이루어진 식별자로 식별된다.

이러한 배분 메서드를 호출할 때 지정하는 것은 인스턴스화할 스레드 그룹의 개수이지 개별 스레드들의 개수가 아님을 주의하기 바란다. 한 그룹당 인스턴스화될 스레드들의 개수는 계산 셰이더 프로그램의 주 함수 앞에서 numthreads라는 HLSL 함수 특성으로 지정한다. 배분 메서드에서처럼 이 함수 특성도 3차원 배열의 세 차원의 크기를 지정하나, 그 배열은 스레드 그룹들이 아니라 실제 스레드들을 담는 것이다. 

각 차원의 크기는 셰이더 모형에 따라 다른데, cs_5_0의 경우 x와 y가 1이상이어야 하고 z는 반드시 1 ~ 64 범위 이어야 한다. 또한 그룹의 전체 스레드 개수(X * Y * Z)가 1024를 넘으면 안된다.


[numthreads(10, 10, 2)]

이 예에서는 각 스레드 그룹마다 총 10 x 10 x 2 = 200개의 스레드가 인스턴스화된다. 이 스레드들의 개별 스레드 역시 차원 색인 ( 0 ~ 차원 크기 - 1) 세 개의 고유한 조합으로 이루어진 식별자로 식별된다. 앞에서처럼 Dispatch(4, 6, 2)라고 호출한 경우, 계산 셰이더 단계 전체적으로 인스턴스화되는 스레드는 48 x 200 = 6400개이다.


스레드 식별 체계

앞에서 처럼 총 6400개의 스레드가 실행된다면 이 스레드들은 자신이 처리할 자료를 어떻게 알게 될까? 모든 인스턴스가 같은 핵(셰이더 프로그램)을 실행하므로, 셰이더 프로그램 소스 코드 자체에서 자료를 지정할 수는 없다. 따라서 처리할 자료 집합을 스레드에게 알려주는 다른 어떤 메커니즘이 필요하다.

앞에서 이야기한 기하학적 스레드 조직화 방식이 바로 그러한 메커니즘의 기반이 된다. 스레드들을 그렇게 조직화하는 데에는 다 이유가 있었던 것이다. 앞에서 보았듯이, 하나의 배분 호출에 관여하는 스레드 그룹들 중 특정 스레드 그룹을 3차원 좌표 형태로 지정하는 것은 아주 간단한 문제이며, 특정 스레드 그룹의 특정 스레드를 지정하는 것 역시 마찬가지로 간단하다.

결과적으로, 한 배분 호출의 모든 스레드를 각각 고유한 식별자로 간단하게 식별할 수 있다.

셰이더 프로그램을 작성할 때 개발자는 셰이더 함수의 입력 매개변수로 쓰일 일단의 시스템 값 의미소들을 지정할 수 있는데, 그런 시스템 값 의미소들 중에는 현재 실행되는 셰이더 프로그램 인스턴스(스레드)를 식별하는 데 사용할 수 있는 것들이 있다. 


SV_GroupID : 이 배분 호출의 스레드 그룹들 중 현재 스레드가 속한 그룹의 3차원 식별자(uint3)

SV_GroupThreadID : 그 스레드 그룹 안에서의 현재 스레드의 3차원 식별자(uint3)

SV_DispatchThreadID : 전체 배분 안에서의 현재 스레드의 3차원 식별자(uint3)

SV_GroupIndex : 현재 스레드가 속한 스레드 그룹의 3차원 식별자를 1차원으로 직렬화한 색인(uint)


각 스레드의 실행마다 이러한 식별 정보가 주어지므로, 개발자는 이들을 이용해서 입력 자료 중 현재 스레드가 처리할 부분을 결정하도록 셰이더 프로그램을 작성하면 된다. 



이 코드가 하는 일은 1차원 버퍼의 자원의 값을 두 배로 증가 시키는 계산 셰이더이다.

이 코드는 스레드 그룹의 크기를 numthreads 함수 특성으로 선언한다. 그 다음 부분은 계산 셰이더의 처리 핵에 해당하는 주 함수이다. 이 함수의 매개 변수는 앞에서 말한 스레드 식별용 시스템 값 의미소 특성들 중 하나이다. 


728x90
728x90

앞에서 대략적인 렌더링 파이프라인에 대해 보았지만, DirectX11의 파이프라인에는 좀 더 광범위한 분야에 적용할 수 있는 유연한 계산을 위한 파이프라인 단계가 있다. 그 단계를 이용하면 GPU를 광선 추적이나 물리 시뮬레이션 같은 다양한 응용 분야에서 사용할 수 있고, 인공지능 계산에도 사용할 수 있다. 그것은 계산 셰이더(Compute Shader) 단계로 흔히 DirectCompute라고 부르는 기술이 바로 이 단계에서 구현된다.


DirectCompute

DirectCompute는 GPU가 제공하는 대규모 병렬 계산 능력을 통상적인 래스터 기반 렌더링 이외의 분야에 활용하기 위한 새로운 처리 패러다임이다. GPU는 병렬로 작동하는 아주 많은 수의 소규모 처리기들로 이루어져 있기 때문에 크게 병렬화할 수 있는 계산 과제에 아주 적합하다.

DirectCompute는 Direct3D 11 처리 환경에 직접 내장되어 있으며, Direct3D 렌더링 API와 기존 프레임워크의 상당 부분을 공유한다. 덕분에 DirectX11에 익숙한 응용 프로그램 개발자라면 응용 프로그램의 설정과 실행에 필요한 정도의 API 사용법은 따로 배우지 않아도 될 정도이다.


계산 셰이더 단계

계산 셰이더의 전체적인 사용 방법은 개념적으로 다른 프로그램 가능 셰이더 단계와 동일하다.

자원들을 순서 없는 접근 뷰(UAV)를 이용해서 계산 셰이더 단계에 연결할 수도 있다. 그러나 계산 셰이더 단계는 다른 프로그램 가능 파이프라인 단계들과 근본적으로 다르다. 가장 큰 차이점은 다른 파이프라인 단계의 출력을 입력으로 받지 않고, 출력을 다른 파이프라인 단계에 넘겨주지도 않는다. 몇 가지 시스템 값 의미소들은 입력 매개변수로 받을 수 있지만, 이는 셰이더 프로그램에서 사용할 수 있는 특성 형태의 자료에만 국한된다. 그 외의 모든 자료 입 · 출력은 자원을 통해서만 일어난다.

이러한 구조 때문에 계산 셰이더에서는 하나의 프로그램 안에서 알고리즘을 완결적으로 구현해야 한다. 




728x90
728x90

자원 뷰 (Resource View)

자원 뷰는 자원을 파이프라인에 묶는 데 사용되는 객체이다. 


자원 뷰의 종류

자원 뷰는 4개의 종류가 있다. 


렌더 타겟 뷰 ( ID3D11RenderTargetView )

깊이/스텐실 뷰 ( ID3D11DepthStencilView )

셰이더 자원 뷰 ( ID3D11ShaderResourceView )

순서 없는 접근 뷰 ( ID3D11UnordereAccessView )


같은 자원이라도 어떤 자원 뷰로 연결되는가에 따라 자원의 용도가 달란진다. 


렌더 타겟 뷰 ( ID3D11RenderTargetView )는 렌더링 파이프라인의 출력을 받을 자원을 연결하는 데 쓰인다. 전통적으로 렌더 타겟은 2차원 텍스처이나, 다른 종류의 자원을 렌더 타게으로 연결하는 것도 가능하다. 렌더 타겟 뷰의 구성 옵션으로는 자원의 DXGI 형식이 있으며, 그 외에도 자원의 종류에 따라 다양한 구성 옵션들이 존재한다. 렌더 대상 뷰는 또한 자원의 일부분을 파이프라인에 노출시키는 다양한 메서드들도 제공한다.


깊이/스텐실 뷰 ( ID3D11DepthStencilView )도 렌더 타겟 뷰처럼 렌더링 파이프라인의 출력을 받는 자원을 위한 것이다. 차이는, 렌더 대상 뷰가 색상 값들을 담는 버퍼를 위한 것인 반면 깊이/스텐실 뷰는 깊이와 스텐실 값들을 담는 버퍼를 위한 것이라는 점이다. 깊이/스텐실 버퍼 자원은 빈번하게 쓰이는 렌더링 연산이 깊이 판정과 스텐실 판정을 수행하는 데 사용되며, 이 때문에 파이프라인의 효율성에 아주 중요한 요인이 된다.


셰이더 자원 뷰 ( ID3D11ShaderResourceView )는 파이프라인의 프로그램 가능 셰이더 단계가 자원을 읽을 수 있게 한다. 이 뷰는 예전에 픽셀 셰이더에서 텍스처가 하던 역할, 즉 셰이더 프로그램 안에서 읽고 사용할 수는 있지만 기록하지는 못하는 자료에 해당하는 것이다.


순서 없는 접근 뷰 ( ID3D11UnordereAccessView )는 셰이더 프로그램안에서 자원을 읽음과 동시에 쓰기도 할 수 있다. 게다가, 출력 장소가 미리 정해져 있지 않기 때문에 셰이더 프로그램 안에서 자원 안의 임의의 위치에 scatter 연산을 수행하는 것도 가능하다. 이 자원 뷰는 픽셀 셰이더 단계와 계산 셰이더 단계에만 사용할 수 있다.


자원 뷰의 생성

자원 뷰의 생성에도 ID3D11Device가 사용된다. 이 인터페이스는 각 자원 종류별 생성 메서드를 제공하는데, 이 생성 메서드들은 모두 같은 패턴을 따른다. 자원 뷰 생성 메서드들은 모두 세 개의 매개변수를 받는데, 첫 매개변수는 자원 뷰를 적용할 자원(예를 들면 Texture)을 가리키는 포인터이다. 둘째 매개변수는 자원 뷰를 서술하는 구조체를 가리키는 포인터로, 그 구조체는 해당 종류의 자원 뷰를 위한 모든 옵션을 담는다. 셋째이자 마지막 매개변수는 해당 종류의 자원 뷰 객체에 대한 포인터로, 호출이 성공한 경우 그 포인터가 가리키는 곳에 자원 뷰 객체가 만들어진다.


자원 뷰의 사용

자원 뷰의 구조체는 사용 용도가 다르기 때문에 정의된 구조체도 모두 다르다. 

각 해당 자원에 대해 자원 뷰를 이용하여 접근하면 된다.


자원 뷰의 해제

자원과 마찬가지로 COM 인터페이스를 따라가므로, 참조를 해제해야 한다.

728x90
728x90
728x90

'좋은 사이트' 카테고리의 다른 글

사이트 - Hooking  (0) 2021.01.19
사이트 - Vulkan 강좌  (0) 2020.10.25
사이트 - wxWidget  (0) 2017.09.04
사이트 - DirectX 강좌  (0) 2016.04.19
사이트 - 무료 수학 강의 사이트  (0) 2016.03.07
728x90

추측항법(Dead-reckoning)이라고 함은 GPS와 같은 로봇의 위치인식 센서에서 로봇의 위치가 들어오지 않을 때 바퀴의 이동량만으로 로봇의 위치를 추측하는 방법을 말합니다. 로봇의 미끄러짐이 없고 바퀴의 크기와 회전각 등을 정확히 알고 있다고 하면 추측항법 만으로도 정확한 로봇의 위치를 계산할 수 있겠지만, 로봇 모델링의 오차와 주변에서 발생하는 각종 에러 요소에 의해 추측 항법으로 계산한 위치에는 에러가 누적됩니다. 이를 보정하기 위해 DGPS, North Star, U-SAT와 같은 각종 위치인식 센서를 사용하여 칼만필터로 정합하는 과정을 거치게 됩니다.


여기서는 추측항법을 위한 바퀴의 이동량을 적분하는 3가지 방법에 대하여 소개합니다. 자세한 내용은 참조 문서를 참조하시기 바랍니다.

 

Odometric_Localization.pdf


출처 : http://blog.daum.net/pg365/112

728x90

+ Recent posts