후하하핫
9k
2022-03-18 15:40:57
4
3267

Neural Compiler: 컴파일러와 컴퓨터의 가능한 미래


오늘은 컴파일러와 컴퓨터에 가능한 미래인 Neural Compiler에 대해서 얘기해 보려 합니다. 제가 볼 때는 컴퓨터 공학의 역사가 바뀔 수 있는 분야이지만, 이미지 처리와 같은 것들보다 덜 알려져 있는 것 같아 공유합니다.


1. 배경


컴파일러는 고수준 프로그래밍 언어를 기계어로 "번역하여 편집"하는 기계입니다.


번역이란 무엇인가요? 번역이란 특정 언어를 다른 언어로 변환 시켜주는 행위를 말합니다. 구체적으로는, C 언어를 통해 Hello, World! 를 출력하는 프로그램을 작성하면, compiler 는 이것을 어셈블리 언어로 변환합니다. assembler는 해당 assembly source code를 1:1 매핑이 되는 이진의 기계어 덩어리, 즉 object file로 변환합니다.


자, 변환이 끝났습니다. 그 다음은요? "편집" 입니다. 이 편집을 linking and relocation이라고 부릅니다.Relocation도 물론 중요하지만, linking은 진짜 중요합니다. 컴퓨터공학의 역사가 linking 위에 쓰여졌다고 말해도 될 정도로요.


자세한 설명을 위해 우리가 Hello, World! 스트링을 출력하는 프로그램을 통해 컴파일 되는 과정을 알아보죠. 아래와 같이요.


#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    return 0;
}

1.1 Preprocessor


1: 처럼 우리는 #include <stdio.h> 라는 header inclusion을 위한 preprocessor directives 수행합니다. 이건 별게 아니고, 그냥 stdio.h를 기계적으로 복붙하는 것에 지나지 않습니다.

컴파일 과정에서 첫 스텝인 preprocess (UNIX 기준 `cpp` command) 는 모든 preprocessor directives를 풀어 (resolve) 줍니다. #include 면 복붙을 하고, #if #else #endif 면 조건에 따라 살아 남을 놈들을 살리는 식이죠.


1.2 Compile


그 이후에는 우리가 아는 그 컴파일러가 나타나서 (UNIX 기준 `cc` command) 소스코드를 컴파일 대상 아키텍쳐의 ISA 어셈블리언어로 변환합니다. 구체적으로는 lexical analysis 라고 해서 토큰화 등 "문자열" 차원에서 이루어 져야 하는 전처리를 수행합니다. 그 후 syntax analysis 를 수행해 "구문론적"인 처리를 합니다. 문법에 따라 토큰을 이해합니다. 우리가 마주치는 컴파일 에러라고 부르는 많은 것들은 이 구문론적 에러, 즉 syntax error 입니다.


만약 syntax error가 발생하지 않으면, 구문론적 이해를 바탕으로 타겟하고 있는 ISA에 맞는 어셈블리언어를 만들어 냅니다. 이게 컴파일입니다.


1.3 Assemble


어셈블리를 기계어로 바꿔줘야 합니다. 어셈블리 언어는 1:1 대응 언어로, ISA에 따른 어셈블리 언어로 작성된 source code를 그에 대응하는 이진 코드로 변환합니다. 변환하는 이유는, 어셈블리 언어는 컴퓨터 입장에서는 비효율적이기 때문입니다. 예를 들어, `add r1 r2` 라고 하면, add의 한글자 글자가 1byte, 띄어쓰기도 1byte를 차지합니다. 총 9 byte입니다. 얘를 0xfe32a34e 와 같이 변환하면 4바이트 안에 표현할 수 있죠. 또한 add64 같은 명령어가 등장하면 명령어의 길이가 왔다갔다 합니다. 컴퓨터는 딱 정해진걸 좋아하기 때문에, 역시 이진 코드가 더 낫습니다.


1.4 Linking


Linking이란 특정 object file에 있는 symbol과 그 symbol의 구체적인 주소를 찾아주는 행위입니다. 제가 작성한 Hello, World 코드를 보시면 printf 라는 함수를 구현체 (body) 가 아닌 선언 (or prototype) 만 갖고 사용할 수 있습니다. 실제로 stdio.h 파일에 들어가보면 구현체가 없고 아래와 같이 나와 있습니다.


/* Write formatted output to stdout.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int printf (const char *__restrict __format, ...);


우린 당연하게 쓰지만, 이거 되게 이상한겁니다. 대체 내가 쓴 함수가 어딨는 줄 알아서 찾아주는걸까요?

Linker가 그 짓을 해줍니다. printf 라는 symbol 을 static library (윈도우즈에서 .lib, UNIX에서 .a) or dynamic library (윈도우즈에선 .dll, UNIX에서는 .so) 그 body를 찾아 연결해 줍니다. 컴파일러를 직접 만들어 보거나 컴파일러의 파라미터를 디벼보셨다면 느끼시겠지만, 이 linking을 효율적으로 하는게 참 어렵습니다. 가장 간단하게 푸는 방법은 무식하게 다 찾아보는겁니다. 그래서 대형 프로젝트일수록 link가 겁나 오래 걸립니다.


이 이후에 실행 파일 (Windows .exe 파일, UNIX a.out 등 실행 파일) 을 만들기 위해서 relocation 해서 집어넣고 하는 과정, dynamic link를 하는 과정도 있는데 생략하겠습니다.


1.5 인터프리터 언어 vs 컴파일 언어

우리가 많이 쓰는 인터프리터 언어인 JavaScript, Python 과 컴파일러 언어인 C, C++, Rust 의 차이는 "번역"만 하느냐, "편집"도 하느냐에 달려 있다고 보시면 됩니다. 인터프리터 언어는 실행기가 달려 있고, 그 실행기가 알아들을 수 있는 번역을 만들어서 인터프리터에게 던지면 됩니다. 컴파일러 언어는 아예 결론적인 실행파일을 만듭니다.


Java가 참 재밌는 언어인 이유는, 이 중간에 속하기 때문입니다. 분명 .class 파일의 형태로 object file 비슷한걸 만들긴 하는데, 결국 얘는 JVM을 돌리기 위한 중간 언어이고, 분명 JVM은 인터프리터에 가까운 성질을 가지고 있습니다. 그래서 컴파일러 학자들 중 상당수는Java도 인터프리터 언어라고 얘기하고, 반대로 실전을 뛰시는 분들은 "그게 뭔 상관이냐 .jar 만들고 .class 만들면 사실상 컴파일러 언어를 쓰는거랑 차이가 없는데" 라는 의견을 보이시기도 합니다.


2. 그래서, Neural Compiler가 뭔데?


사실 이걸 설명하기 위한 배경지식인데 좀 길었네요.


딥러닝 얘기하면 길어지니, 그냥 Neural 이 붙으면 뭔가 인공지능, 구체적으로는 인공신경망이라는 방법을 적용했다, 라고 생각하시면 됩니다.


2017년에 제안된 Neural Compiler의 아이디어는 심플합니다. 우리가 프로그래밍 언어를 주면, 이 언어를 인공지능이 알아들을 수 있는 방법으로 내부에서 컴파일하고, 우리가 가진 하드웨어에서 돌아가는 ISA 기반 어셈블리 언어 소스코드를 뱉겠다는겁니다. 굉장히 도전적인 방법이죠.


이걸 처음부터 끝까지 할 수 없기 때문에, 일단 학자들은 인공지능에게 더 쉬운(?) 일들부터 시켜보고 있습니다. 예를 들어서, Binary Search 를 인공지능에게 시켜보는겁니다. 입력은 정렬된 sequence와 찾고 싶은 값, 출력은 해당 값에 해당하는 인덱스입니다. 이걸 위해 입력과 출력을 인공지능이 알아들을 수 있는 형태로 바꾸어 줘야 합니다.


그럼 인공지능이 알아 들을 수 있는 형태란 뭘까요?


3. 수학적 표현: 행렬과 벡터


인공지능은 쉽게 말하면, 벡터 형태의 입력을 받고, 해당 입력을 행렬 및 여러 수학적 함수들 (구체적으로는 미분가능한 함수들) 로 뚜쉬뚜쉬 해서 벡터 형태의 출력을 내놓습니다.


즉, 인공지능의 입장에서 얘가 알아들을 수 있는 형태는 "벡터" 혹은 "벡터의 집합", 더 나아가서 "벡터의 함수"입니다. 결국 지금 학자들이 풀고 있는 문제는 우리가 쓰고 있는 "프로그램이라는 것을 어떻게 수학적으로 표현할 것인가" 입니다.


4. Neural Compiler가 왜 좋아요?


사실 제가 컴파일러 배경지식에서 빼먹은 부분이 Compiler Optimization, 즉 컴파일러 최적화 부분입니다. 컴파일러 최적화는 컴파일러가 뱉은 어셈블리어를 그대로 쓰지 않고, 순서를 바꾸거나 필요 없는 부분을 없애는 등 의미가 변하지 않는 범위 안에서 더 최적화 된 어셈블리어 소스코드를 만드는 행위입니다. 컴파일러 최적화 때문에 발생하는 차이 때문에 나타나는 것이 Debug 모드와 Release 모드의 동작이 달라지는 것입니다.


자, 그럼 Neural Compiler는요? 두가지 관점에서 좋을 수 있습니다.


첫번째로, 컴파일러 최적화 자체를 인공지능 기반으로 바꿉니다. 지금은 최적화 방법을 사람들 머리에서 나온 방법으로 하고 있는데, 딥러닝한테 최적화를 시켜보자는 겁니다. 바둑도 두는데 이것도 잘하겠지, 하는 생각이죠.


두번째는 좀 radical한데, 아예 기존의 CPU, GPU와 같은 프로세서의 개념을 버리자는겁니다. Tensor Processing Unit (TPU) 혹은 Neural Processing Unit (NPU) 등의 인공지능 가속기 하나만 있으면 소프트웨어가 다 돌아갈 수 있도록 하면, 굳이 회사들이 정의한 ISA 같은 것에 갇혀있을 필요가 없다는거죠.


지금은 연구단계이고, 조금 요원하긴 합니다만, 정말 Neural Compiler가 잘 되면 컴퓨터 공학 이라는 것 자체가 바뀔 수 있습니다. 심지어 "컴퓨팅" 이라는 단어 자체가 의심 받을지도 모르죠.


5. 왜 이런 얘길 했냐?


일단, 컴퓨터 공학 자체에 관심이 있으신 분들 재밌으라고 썼습니다.


두번째로, 세상이 바뀌는걸 보면 인공지능은 그냥 buzz word 이상입니다. 정말 세상 전체가 바뀔 수 있는 포텐셜이 있습니다. 최전선에 있는 한명의 학자로서, 저게 되면 내가 알던 세상이 아니게될거라는 생각이 매주 듭니다. 빅테크들이 엄청 빠르게 세상을 바꾸고 있습니다.


제가 추천드리는건, 살아남기 위해서 꼭 선형대수학과 인공지능 공부를 하세요. 프로그래머라는 직업이 사라지는게 진짜 불가능한 일이 아니라는 것을 실감하고 있고, 비단 이건 저만의 걱정은 아닌 것 같습니다.

3
  • 댓글 4

  • 제운
    2k
    2022-03-22 12:43:38 작성 2022-03-22 12:44:13 수정됨

    좋은글 감사합니다. 저를 가르쳐주시고 계신 선생님도 나중을 생각하면 x86 어셈블리 언어 책은 꼭 여러번 보라고 하시고, si시장은 인공지능이 커지면 산업혁명때처럼 붕괴할수 있다는 예측을 하셨죠.

    인공지능을 배울때 보통 프로그래밍 공부하듯 인터넷으로 정보찾아보며 배우는게 나을까요? 아니면 한 2년 투자한다 생각하고 대학원에 들어가는게 좋을까요?

  • 후하하핫
    9k
    2022-03-22 12:48:14

    @제운: 인공지능 개론서들이 많이 있는데요, 제가 작성한 다음 글과 댓글들을 참고하세요.

    - https://okky.kr/article/1069196


    공부하는 태도는 다음 글들을 참고하세요.

     - https://okky.kr/article/1182404 

     - https://okky.kr/article/1123767


  • 이스
    93
    2022-03-22 13:33:28

    항상 좋은 글 감사합니다.

    항상 남들이 만들어 놓은 정보도 제대로 소화하지 못하지만..

    조금씩 스스로 사고 하는 버릇이 생기고 있습니다.

    감사합니다!

  • 제운
    2k
    2022-03-22 17:02:59

    감사합니다

  • 로그인을 하시면 댓글을 등록할 수 있습니다.