ㅎㅇ

고3 대입 시기에 팩토리오 800시간을 찍고 CPU를 버전 7까지 만들었다가

컴퓨터공학과에서 2년을 나름 열심히 수련하게 되고 팩토리오에서 점점 멀어짐.

그러다 군입대를 결정하고 휴학을 때려서 지금은 밥을 똥으로 바꾸는 기계가 되어버렸는데

이참에 다시 팩토리오에서 컴퓨터나 만들어볼까 하고 글을 쓰게 됨.

팩토리오에서 어떻게 하면 컴퓨터를 잘 설계할 수 있을 거 같은지에 대한 내 생각을 정리한 글임.



[팩토리오 회로 설계의 특이사항]


1. 60틱의 제약

회로는 1초에 60번보다 빠르게 돌 수 없음. 현재 CPU를 대충 3GHz라고 가정하면 5천만 배 느린 속도임.

따라서 이 틱을 정말 최대한 아껴써서 속도를 뽑아내야 함.


2. 10만개의 제약

컴마다 다를 수는 있는데 조합기 10만개정도 박으면 렉걸림. 이 안에서 써야 함.

모든 조합기를 하나의 연산에 투자한다면 초당 6백만개의 연산이 가능한데, 따라서 어떤 연산이든 6M operation/s 가 성능의 이론상 한계가 될 것임.

실제로는 연산을 위한 부가 장치가 필요하므로 GPU같이 매우 병렬화된 하드웨어를 만든다고 해도 1Mops를 넘기 힘들 것이라고 생각함.

이 성능이 이론상 최대라고 생각하고 목표 성능을 결정해야 함.

예를 들어 1024*1024의 이미지를 처리한다고 해도 어떤 구조에서든 아주 이상적으로 봐도 1초에 한 장 이하의 속도로 처리할 수 있을 것임.


3. 컨트롤은 비싸고 연산은 싸다

덧셈/곱셈도 1틱, 나눗셈에 거듭제곱까지 전부 1틱이지만 조건이 참일 때 1을 출력하는 것도 1틱임.

따라서 현실보다 연산 장치는 저렴해지고 제어 장치는 비싸짐.

이를 고려해서 최대한 단순한 제어를 해야 속도를 높일 수 있음.


4. 모든 것이 무조건 1틱, 겹치면 더해짐

팩토리오는 모든 회로가 정확히 1틱에 한 번 동작함.

따라서 여러 신호 중에 누가 더 먼저 도달하는지 경쟁해서 결과가 달라지는 경우가 없음.

이것을 잘 활용해야 함.

특히 저장장치는 되먹임 회로가 사용되는데 여기서 이득을 많이 볼 수 있음.

또한 신호 여러 개가 겹치면 더해진다는 성질도 굉장히 범용적이고 효과적으로 사용할 수 있음.

덧셈 연산을 몇 번이고 거의 공짜처럼 사용 가능한 사기 기능임.


5. 여러 유형의 신호를 병렬로 처리 가능

조합기엔 각각 기능이 있어서 여러 유형 신호를 병렬로 처리 가능함.

이를 잘 이용하면 조합기 개수를 매우 많이, 최대 256배 줄일 수 있음.

특히 저장 장치와 신호 전송(버스)에서 그럼.



[생각하고 있는 구조]


아래는 생각하고 있는 컴퓨터 시스템의 구조임.



1. 단일 프로세서에 단일 스레드, 운영체제 없음

아무리 생각해 봐도 팩토리오에서 여러 스레드나 프로세스를 굴릴 일은 일어날 거 같지 않음.

그래서 스레드/프로세스 1개만 돌릴 수 있는 시스템으로 제한해서 만드려고 하고

자원을 나눠줄 놈도 하나니 운영체제도 없는 시스템으로 가려고 함.

대신에 프로그램마다 시그널 핸들러를 넣어서 인터럽트 시 호출되도록 하는 기능 정도는 있으면 좋을 거 같음.


2. 프로그램이 별도의 공간에 존재하는 하버드 아키텍쳐

아래서도 설명하겠지만 이 시스템이 실행하는 프로그램은 현실의 x86같은 명령어 집합 구조보다 훨씬 저수준의 명령들을 실행할 것임.

따라서 그 명령어를 직접 하나하나 넣어주는 것은 굉장히 굉장히 비효율적인 일이 됨. (전에 실제로 했는데 CPU 버전 하나 설계하는 것만큼 걸림...)

그래서 컴파일러가 필수적이고, 외부의 진짜 CPU에서 도는 진짜 SW인 컴파일러에서 컴파일을 마친 후 블루프린트를 뽑아 인게임에 넣어 주는 방식이 가장 효율적임.

그랬을 때 굳이 프로그램을 데이터와 합칠 이유가 없어짐.

따라서 프로그램은 주로 일정신호조합기로 구성된 별도의 ROM을 게임 외부에서 생성된 블루프린트로 넣어주는 방식으로 동작함.


3. CPU, GPU 존재

CPU의 코어는 1개지만 병렬화가 가능한 연산, 예를 들면 그래픽 연산 등을 처리하기 위한 별도의 GPU가 있으면 좋을 거 같다는 생각을 해봄.

GPU프로그램은 현실처럼 CPU에서 넣어주는 것이 아닌, 위처럼 GPU 내부의 ROM을 블루프린트로 찍는 방식으로 입력됨.


4. 보다 저수준의 명령어 집합 구조

위 시스템에서 실행하는 명령어 집합 구조는 x86이나 ARM등의 현실 사례보다 훨씬 저수준임. (심지어 micro-op보다도!)

왜냐하면 위에서 언급한 '컨트롤은 비싸고 연산은 싸다'는 특성으로 인해 컨트롤 유닛의 역할을 최대한 줄여야 하기 때문임.

그래서 'A에서 B로 신호가 가는 길을 열어라' 라던지 'X번째 틱에 Y에 신호를 X만큼 쏴라' 처럼 상당히 저수준의 명령어를 실행함.

이로 인해 기계어로 직접 코딩하기가 현실보다 훨씬 복잡해짐. 디버깅은 토 나옴.

그래서 컴파일러를 굉장히 잘 설계해야 하며 게임 외부에서 가상 머신과 디버거를 만들어 보는 것도 고려해 볼 수 있음.


5. 파이프라인화 적음

현실의 CPU가 수십, GPU는 수백 수천(아마?) 개의 파이프라인을 가질 수 있는 것에 비해

이건 파이프라인화가 적음.

왜냐면 애초에 메모리 병목이랑 연산 병목이 없음. 모조리 1틱임.

그래서 파이프라인도 적고, 따라서 분기예측이나 겹겹이 쌓인 캐시. 캐시 prefetch 등도 필요없음.


6. fused연산의 적극적인 사용

컨트롤이 비싸므로 최대한 적은 컨트롤로 많은 연산을 해야 함.

그래서 연산 여러 개가 결합된 연산을 기본 기능으로 채택하면 좋음.

현실에서는 곱셈과 덧셈이 합쳐진 FMA(fused multipliy accumulate) 연산이 있는 것처럼

곱셈 후 덧셈 / minmax / 병렬로 비교 후 and와 or 등의 연산을 만들면 성능이 좋아짐.


7. 레지스터 - L1캐시 - 통합 메모리 - 스토리지의 4단 메모리 구성

팩토리오에서는 모든 소자가 조합기로 통일되어 있음. (물론 기차나 벨트같은 걸 쓸 수도 있는데 성능은 매우 느리고 별 장점이 없다 생각함)

그래서 현실에서처럼 서로 다른 특성을 가진 소자별로 계층적 메모리를 구성하는 전략이 덜 유효할 수 있음.

대신에 컨트롤이 비싸기 때문에 신호를 여러 군데로 바로 보낼 수 있도록 이곳저곳에 연결된 레지스터를 두면 상당히 유용함.

어차피 주로 쓰는 변수는 16개나 32개를 넘는 경우가 적으니까.

레지스터는 메모리의 0번지부터 시작하는 자리에 고정적으로 매핑됨.

그리고 그 아래에 일반적인 메모리 역할을 하는 L1캐시를 둬서 한 번에 한두개씩 데이터를 뽑아올 수 있게 함.

캐시 히트를 판정하는 컨트롤은 비싸므로 이건 명시적으로 컨트롤하도록 했음.

또 CPU의 L1캐시와 GPU의 SM들이 서로 통신할 필요가 있으므로 통합 메모리를 둬서 통신함.

통합 메모리와, L1캐시와 SM의 메모리 사이는 한 번에 데이터를 덩어리째로 옮길 수 있도록 설계할 수 있음.

그리고 마지막으로 각각 연산을 활용해서 한 개의 셀에 하나가 아닌 여러 개의 데이터를 담을 수 있도록 했지만 속도가 떨어진 스토리지가 있으면 좋을 것임.

그래서 메모리는 이렇게 4계층으로 구성됨.


8. MMU에 관해

솔직히 프로세스 하나만 돌아가고 해서 MMU가 그렇게 필요해 보이진 않음. 그냥 다 수동으로 조작하면 되지 않나라는 생각.

그래도 IO라던지에 유용할 수도 있고 나름 또 쓸만할 수도 있으니 그냥 그림에 넣어봄.


9. I/O에 관해

I/O는 잘 모르긴 한데 memory mapped I/O처럼 만들면 빠를 거 같음. 아니면 아예 고정적인 주소 매핑을 박아놓던가.



10. GPU 구조에 관해

GPU는 상당히 유용할 수 있고 현실과 비슷하게 설계할 수 있음. 제어보단 연산이 중심이므로 더 현실 구조와 가까워짐.

대신에 레지스터 블럭의 크기와 개수를 고정해 놓았는데, 이건 충분히 가변적으로 할 수 있어보임.

버전이 올라가면 바꿀 거 같지만 그럼 구조가 꽤 복잡해질 거 같아 최초 버전은 일단 고정하는 걸로. 어차피 성능은 크게 차이 안 날 거 같으니까.

다른 구조는 다 현실과 똑같아보이지만, Sync.(Synchronized) Mem이라고 다른 걸 하나 박아놨음.

저게 뭐냐면, 팩토리오는 신호 여러 개가 동시에 들어오면 현실처럼 망하는 게 아니라 더해지므로

덧셈 reducing을 굉장히 빠르고 간단하게 할 수 있다는 사기성을 지님.

그래서 동시에 같은 메모리를 읽거나, 같은 곳에 덧셈 reducing 할 때 쓸 수 있는 메모리를 하나 두고 동시에 쓴다는 의미에서 이름을 저렇게 붙임.

현실에서는 const 메모리에서 읽기밖에 안 되지만 여기선 쓰기도 가능하지.



이상 끝임.

팩토리오 커뮤니틴데 컴퓨터 공학 내용을 올려서 미안함. 문제되면 글 내림.

다른 의견 주면 감사하겠음.

근데 이걸 언제 다 구현할 수 있을지 모르겠네