출처 : https://www.youtube.com/watch?v=JRcXUuQYR90
--
요약
✅ 저자 왈 : Mojo가 하는 일을 하는 언어는 세상에 없습니다.
✅ MLIR 기반의 코드 생성: Mojo는 MLIR을 기반으로 제작되었습니다. MLIR은 도메인 특화 컴파일러를 빠르게 만들 수 있는 핵심 컴파일러 인프라를 제공합니다
✅ 하드웨어 가속: Mojo는 SIMD, 텐서 코어, VNNI 등 최신 하드웨어의 가속 기능을 직접 활용할 수 있도록 설계되었습니다. 라이브러리를 통해 이러한 기능을 쉽게 사용할 수 있으며, 컴파일 타임에 하드웨어에 맞게 코드를 최적화합니다.
✅ 타일 기반 프로그래밍 모델: Mojo는 타일 기반 프로그래밍 모델을 지원합니다. 개발자는 타일 단위로 알고리즘을 작성하여 메모리 계층 구조를 효율적으로 사용하고 GPU와 같은 가속기를 최대한 활용할 수 있습니다.
✅ 명시적 벡터화: Mojo는 명시적 벡터화를 지원합니다. 개발자는 벡터 연산을 직접 사용하여 코드를 더욱 빠르게 만들 수 있습니다.
✅ 병렬 처리 지원: Mojo는 병렬 for 루프, async-await, 고성능 스레딩 라이브러리 등 다양한 병렬 처리 기능을 제공합니다. 개발자는 이러한 기능을 사용하여 멀티 코어 CPU와 GPU를 최대한 활용할 수 있습니다.
✅ 값 의미론과 배타적 소유권: Mojo는 Swift와 Rust에서 영감을 받아 값 의미론과 배타적 소유권을 지원합니다. 이를 통해 암시적 복사를 줄이고 메모리 사용량을 최적화할 수 있습니다. 또한 꼬리 호출과 같은 기능을 더욱 효율적으로 구현할 수 있습니다.
✅ 수명 기반 자동 메모리 관리: Mojo는 값이 마지막으로 사용된 직후에 자동으로 파괴되는 수명 기반 자동 메모리 관리를 사용합니다. 이는 참조 카운팅과 같은 전통적인 방법보다 효율적이며, 메모리 누수를 방지합니다.
✅ 컴파일 타임 메타 프로그래밍: Mojo는 컴파일 타임 메타 프로그래밍을 지원합니다. 이를 통해 개발자는 컴파일 타임에 코드를 생성하고 최적화할 수 있습니다.
✅ 제로 비용 추상화: Mojo는 C++, Rust와 같이 제로 비용 추상화를 지원합니다. 즉, 추상화를 사용하더라도 성능 저하가 거의 없습니다.
============
진행자: 오늘 저와 함께 해주신 분은 크리스 랫너입니다. 크리스, 안녕하세요?
크리스 랫너: 안녕하세요. 초대해 주셔서 감사합니다, 크리스.
진행자: 정말 영광입니다. 당신처럼 훌륭한 이력을 가진 게스트는 흔치 않죠. LLVM, Clang, Swift까지 정말 대단한 경력을 가지고 계신데요, 그 자체로도 긴 이야기가 될 수 있겠지만, 이 팟캐스트에서는 항상 미래에 대해 이야기합니다. 그러니 바로 본론으로 들어가죠. 새로운 언어를 만드는 이유를 묻는 건 의미가 없어 보입니다. 새로운 언어를 만드는 건 당연한 수순처럼 보이니까요. 오늘은 왜 Mojo를 만들게 되었는지에 대해 이야기해 보려고 합니다.
크리스 랫너: "새로운 언어를 만드는 게 얼마나 어려운지 알면서, 어떻게 또 그런 엄청난 일을 벌일 생각을 했는가?"라고 질문을 바꿔볼 수도 있겠네요.
진행자: 고생길을 자처하신 거죠.
크리스 랫너: 글쎄요, 저는 어려운 일을 두려워하지 않는 편인 것 같습니다. 하지만 사실은... 다른 언어 개발자들과 마찬가지로 저에게도 나름의 동기가 있습니다. 저에게는 문제 해결이 가장 중요한 동기입니다.
크리스 랫너: 사실 저는 Mojo를 만들기 전에 언어 개발을 꺼렸습니다. Swift 개발 경험, 그 전에는 OpenCL 개발 경험을 통해, 프로그래머의 문제 해결을 위해 일반적으로 문법부터 시작하는 접근 방식이 쉽지 않다는 것을 알고 있었기 때문입니다. Swift의 경우, "자동 클로저와 고차 함수형 프로그래밍 기능을 넣고, LLVM 컴파일 흐름에 통합하고 싶다"는 생각에서 시작했습니다. Mojo의 경우, Modular에서 "GPU를 최대한 활용하려면 어떻게 해야 할까?"라는 질문에서 시작했습니다. 행렬 연산, Bfloat16, AI 확장 기능 등을 갖춘 고성능 CPU가 다양하게 등장하고 있는데, 이들을 어떻게 효율적으로 활용할 수 있을까요?
크리스 랫너: 하드웨어의 변화는 과거에는 CPU가 매년 조금씩 개선되면서 코드를 다시 컴파일하면 이전 코드가 더 빨리 실행되는 식이었습니다. 하지만 오늘날에는 특수 목적 하드웨어 블록이 등장하면서 근본적으로 다른 소프트웨어가 필요하게 되었습니다. 가장 극단적인 예가 GPU입니다. 이러한 하드웨어를 프로그래밍하려면 완전히 다른 종류의 소프트웨어가 필요합니다.
크리스 랫너: Modular에서 처음 시작한 것은 언어 개발이 아니었습니다. 프런트엔드 없이 매우 정밀한 코드 생성기를 만드는 것이었습니다. LLVM의 큰 부분을 대체하고, 고성능 루프 네스트를 합성하는 새로운 방법을 개발했습니다. 그리고 모든 것을 IR로 직접 작성했습니다. 이를 통해 코드 생성 철학과 스택이 제대로 작동하고 원하는 결과를 제공할 수 있다는 것을 증명했습니다. 이러한 확신을 얻은 후에야 문법에 대해 고민하기 시작했습니다.
진행자: MLIR을 직접 작성하는 것은 어떤가요? Mojo가 Swift의 후속작이라면 MLIR은 LLVM의 후속작이라고 할 수 있죠. LLVM은 25년 정도 된 매우 널리 사용되는 기술입니다. "모든 것을 지배하는 하나의 IR이 있어야 한다"는 철학을 바탕으로 개발되었고, 많은 컴파일러 개발자들이 LLVM IR에 익숙합니다. 기본적으로 C와 비슷하게 보이거나 C와 비슷한 형태로 변환할 수 있는 것들을 처리하는 데는 훌륭했습니다.
크리스 랫너: 하지만 GPU, 가속기 등이 등장하면서 여러 추상화 레벨이 필요해졌습니다. Haskell GHC 컴파일러처럼 여러 단계의 저수준 변환 단계를 거치면서 각 추상화 레벨에 최적화 기회를 제공하는 방식이 필요하게 되었습니다. 그래서 2018년에 LLVM을 대체하고 도메인 특화 컴파일러를 빠르게 만들 수 있는 MLIR 프로젝트를 시작했습니다. MLIR은 핵심 컴파일러 인프라, IR 표현, 패스 관리자, 이진 분할 도구, IR 구문 분석 및 출력 기능 등을 제공하여 컴파일러 개발자가 자신의 도메인에 집중할 수 있도록 합니다. MLIR은 하드웨어 합성 도구 등 다양한 도메인 특화 컴파일러를 만드는 데 유용한 만능 도구입니다.
크리스 랫너: MLIR의 장점 중 하나는 텍스트 형식을 지원한다는 것입니다. 텍스트 형식으로 직접 작성할 수 있습니다. 하지만 직접 에러를 작성하는 것은 매우 고통스럽습니다. 사용자에게 좋은 경험을 제공하지 못합니다. 개념 증명을 위해서는 가능하지만, 사용자에게는 절대 노출하고 싶지 않은 방식입니다.
진행자: C보다 저수준인가요, 고수준인가요, 아니면 그냥 다른가요?
크리스 랫너: MLIR은 도메인에 독립적입니다. 원한다면 머신 레벨 코드 표현에 사용할 수 있습니다. 많은 사람들이 가속기 등을 위해 그렇게 사용하고 있습니다. MLIR은 머신러닝 코드 생성 커뮤니티에서 널리 사용됩니다. 하지만 Mojo에서는 AST에 사용합니다. 특정 용도로 사용할 필요는 없습니다. Mojo의 경우, 전통적인 AST를 구축하지 않고 파서에서 직접 MLIR을 생성합니다.
진행자: 그렇다면 하드웨어 측면에서 어떤 작업을 했는지, 그리고 사용자 공간 측면에서는 어떤 작업을 했는지 살펴보겠습니다. 먼저 사용자 공간 측면부터 살펴보죠. 이제 이 접근 방식을 세상에 선보일 준비가 되었다고 말씀하셨는데요, 왜 Python과 유사한 문법을 선택했나요?
크리스 랫너: 네, 코드 생성 방식에 대한 확신을 얻은 후에는 IR을 어떻게 얻을지 결정해야 했습니다. 몇 가지 접근 방식이 있습니다. 프로그래밍 언어를 만드는 것은 매우 어려운 일이지만, 가장 먼저 떠오르는 것은 다른 언어에 내장된 도메인 특화 언어를 사용하는 것입니다. 예를 들어 Python을 사용하고 데코레이터 기반 접근 방식이나 다른 언어의 AST를 탐색하여 IR을 생성하는 방법을 사용할 수 있습니다. 다른 접근 방식은 직접 언어를 만드는 것입니다. 또 다른 접근 방식은 소스 변환기를 만드는 것입니다.
크리스 랫너: 저희는 AI 분야, 고성능 가속기, GPU 등이 급부상하고 있는 분야를 목표로 하고 있습니다. 이 분야는 프로그래밍 모델이 매우 복잡하고 어렵기 때문에 많은 사람들이 어려움을 겪고 있습니다. 그래서 저희는 어려운 길을 선택했습니다. 프로그래밍 언어를 만드는 것이 얼마나 어려운지 잘 알고 있지만, 그만한 가치가 있다고 판단했습니다.
크리스 랫너: 프로그래밍 언어를 만드는 것은 문법이나 컴파일러를 만드는 것만으로 끝나지 않습니다. 코드 포맷터, 디버거, 라이브러리 생태계, 개발자 커뮤니티, 패키지 관리자, LSP, Slack 채널 등 모든 것을 구축해야 합니다. 기술적으로 어려운 부분을 올바르게 구축해야 하며, 설계와 기술적인 측면에서 많은 노력이 필요합니다.
크리스 랫너: Swift 개발 경험을 통해 이러한 어려움을 잘 알고 있었기 때문에 Mojo 개발에서는 위험을 줄이면서도 개발자들이 익숙한 환경을 제공하고 싶었습니다. AI 분야에서는 Python이 대세입니다. 모두가 Python을 사용합니다. 따라서 Python이 아닌 다른 언어를 선택하려면 Python보다 나은 이유를 명확하게 제시해야 합니다. 중괄호나 탭 사용 여부는 좋은 답변이 아닙니다. 개발자들은 이미 Python에 익숙해져 있기 때문입니다. 수억 명의 개발자들이 이미 알고 있는 Python을 사용하는 것은 큰 장점입니다.
크리스 랫너: 엔지니어링 및 설계 관점에서도 매우 유용합니다. 이미 언어의 모습을 알고 있기 때문에 모든 것을 처음부터 다시 고민할 필요가 없습니다. Mojo는 Python의 확장된 버전이지만, 설계 작업은 여전히 필요합니다. 하지만 적어도 우리의 에너지를 집중할 수 있습니다.
진행자: 대부분의 초기 결정은 이미 내려져 있는 셈이죠.
크리스 랫너: 네, 정확히 그렇습니다. 예를 들어 Python은 조건 뒤에 if가 오는 다소 특이한 후위 삼항 연산자를 사용합니다. 개인적으로는 좋아하는 방식은 아니지만, 중요하지 않습니다. 이미 검증되었고, 작동합니다. 이를 변경하려면 호환성을 깨뜨리고, 익숙함을 깨뜨리고, 사람들이 이미 익숙해진 교육 내용을 깨뜨려야 하는 이유를 명확하게 제시해야 합니다. 따라서 Python과 유사한 문법을 선택하는 것은 프로젝트를 매우 편리하게 진행할 수 있도록 합니다.
진행자: 고성능과 GPU 최적화에 중점을 둔 언어를 만든다면 저는 머신러닝, 데이터 과학 분야와 게임 분야 사이에서 고민했을 것 같습니다. PlayStation 5 프로그래머들을 타겟으로 할 수도 있었을 텐데요. 그쪽 분야는 수익성이 높습니다. 혹시 고려해 보셨나요?
크리스 랫너: 사실 그렇지 않습니다. 저희는 GPU, 가속기, AI에 대한 명확한 목표를 가지고 있습니다. 이 문제는 지루할 수 있지만, 저는 이 분야에 5~7년 정도 몸담고 있었기 때문에 그 방향으로 나아가고 있습니다. Mojo는 일반적인 목적의 프로그래밍 언어이기 때문에 다양한 용도로 사용할 수 있습니다. GUI 라이브러리, 웹 서버 등을 만드는 사람들도 있습니다. Mojo는 범용 프로그래밍 언어이지만, 저희의 설계 포인트는 저희의 사용 사례와 해결하려는 문제에 기반을 두고 있습니다. Mojo가 하는 일을 하는 언어는 세상에 없습니다. 다른 사람의 언어를 사용하는 것도 하나의 선택지였지만, 저희는 직접 언어를 개발하기로 결정했습니다.
진행자: Mojo의 차별점에 대해 자세히 알아보기 전에, Python에 익숙한 사람이 Mojo를 사용한다면 얼마나 익숙하게 느껴질지 궁금합니다. 새로운 기능을 사용하기 전까지는 완전히 똑같다고 느껴질까요?
크리스 랫너: Mojo는 아직 완성되지 않았습니다. 개발 과정의 절반 정도 진행되었다고 생각합니다. 하지만 완성되면 Python의 완전한 상위 집합이 될 것입니다. Python 2에서 Python 3로의 전환 과정을 알고 계시다면, 그 과정이 얼마나 힘들었는지, 커뮤니티에 얼마나 많은 에너지를 소모했는지, 얼마나 큰 혼란을 야기했는지 알고 계실 것입니다. 그 이유는 Python 2와 Python 3가 비슷하면서도 달랐기 때문입니다. 패키지 간의 호환성이 없었고, Python 2와 Python 3 코드를 혼합하여 사용할 수 없었습니다.
크리스 랫너: Swift의 경우, Objective-C와 완전히 다른 언어이지만, Objective-C 커뮤니티를 Swift로 점진적으로 마이그레이션하는 데 성공했습니다. 두 언어가 함께 존재할 수 있도록 만들었기 때문입니다. 한 언어에서 다른 언어를 호출할 수 있었습니다. 따라서 사용 중인 타사 패키지는 Objective-C로 유지하고, UI는 Swift로 작성하는 등의 선택이 가능했습니다. 모든 것을 한 번에 옮겨야 하는 부담이 없었습니다.
크리스 랫너: Mojo의 경우, Python의 완전한 상위 집합으로 개발하고 있습니다. 따라서 모든 Python 관용구가 Mojo에서 작동합니다. 현재는 더 많은 동적 기능을 추가하는 데 집중하고 있습니다. 하지만 지금도 임의의 Python 패키지를 가져와서 사용할 수 있습니다. 자유롭게 혼합하고 일치시킬 수 있습니다.
진행자: 대부분의 초기 결정은 이미 내려져 있는 셈이죠.
크리스 랫너: 네, 그렇습니다. Python은 C 문법 및 접근 방식과의 상호 운용성을 지원하는데, Mojo도 이를 지원하나요?
크리스 랫너: 아직 결정하지 못했습니다. Python 프로그램의 관점에서 Mojo를 살펴보면, Mojo가 Python과 유사하고 Python의 모든 동적 기능을 지원하도록 발전하고 있다는 점이 흥미롭습니다. 하지만 고성능 코드를 작성하기 위해 다른 언어로 전환할 필요는 없습니다. Python이나 Objective-C는 완전히 동적인 객체 지향 언어이지만, 중요한 부분은 C 또는 C++로 작성되어 있습니다. NumPy가 좋은 예입니다. 훌륭한 Python API를 제공하지만, 내부적으로는 C, C++로 작성되어 있으며 MKL 등을 래핑하고 있습니다.
크리스 랫너: Mojo에서는 모든 것을 하나의 언어로 작성할 수 있습니다. 전환할 필요가 없습니다. Mojo는 점진적으로 타입을 지정할 수 있는 언어이며, Python의 mypy 타입과는 다른 실제 타입을 가지고 있습니다. 또한 최근 몇 년 동안 개발된 차세대 컴파일러 기술을 활용하고 있습니다. 따라서 C와의 통합, 즉 저수준 바인딩을 지원할지, 아니면 커뮤니티가 Mojo로 전환하도록 유도할지에 대한 질문이 있습니다. 아직 결정하지 못했습니다.
진행자: 좋아하는 주제인 타입에 대해 질문해 보겠습니다. 웹사이트에서 Mojo에는 점진적 타입 시스템이 있다고 읽었습니다. 점진적 타입 시스템이 무엇인지 잘 모르겠습니다. 점진적 타입 시스템은 점진적 타입 시스템과 같은 것이 아니죠?
크리스 랫너: 용어를 정확하게 사용하지 못했을 수도 있습니다. 저는 엔지니어이지 수학자가 아닙니다. Mojo의 타입 시스템에 대해 설명해 주시겠습니까? 옵트인 방식인가요? 어떤 종류의 타입 시스템인가요? 사용하려면 얼마나 배워야 하나요?
크리스 랫너: 모두 좋은 질문입니다. 먼저 타입이 있는 언어가 Python과 어떻게 통신할 수 있을까요? 그 다음 질문은 기존 Python 타입 시스템이 무엇인가요? 일반적인 프로그래머에게 물어보면 Python에는 타입이 없다고 대답할 것입니다. 좀 더 숙련된 개발자에게 물어보면 동적 타입이 있다고 대답할 것입니다. 리스트, 딕셔너리, 문자열, 정수 등이 있지만 모두 런타임에 결정됩니다.
크리스 랫너: 프로그래밍 언어에 대해 잘 알고 정적 언어를 좋아하는 사람으로서, Python에는 하나의 타입이 있다고 말할 수 있습니다. 그 타입은 Python 객체에 대한 참조입니다. Python에는 하나의 타입만 있기 때문에 타입을 명시적으로 지정할 필요가 없습니다. 따라서 Python은 정적 타입 시스템을 가지고 있다고 볼 수 있습니다. 단지 하나의 타입만 있을 뿐입니다.
크리스 랫너: 이러한 관점에서, 이 타입에 이름을 붙일 수 있습니다. Mojo에서는 PythonObject라고 부릅니다. 영원히 PythonObject라고 부를지, 아니면 Object로 줄일지는 아직 결정하지 못했습니다. 하지만 이름이 있습니다. PythonObject입니다. 이제 새로운 타입을 정의할 수 있습니다. int, string 등이 있습니다. int라고 지정하면 다른 타입이 됩니다. 타입 간의 변환이 가능하고, 통합하고 연산할 수 있습니다. 프로그래머는 이것이 int라고 명시적으로 지정할 수 있습니다. 이제 박싱되지 않고, 스택에 할당되며, 머신의 워드 크기 등을 가집니다. C, C++ 등에서 작동하는 방식과 동일하게 작동합니다. 따라서 완전히 정적인 세계를 선택할 수 있습니다.
크리스 랫너: CPU 및 GPU 고성능 수치 프로그래밍을 하는 사람들은 동적인 것을 원하지 않습니다. 머신에 대한 완전한 제어, 매우 까다로운 저수준 최적화에 대한 완전한 제어를 원합니다. 동적 코드를 작성하고 가상화를 해제하려는 시도는 원하지 않습니다. Rust의 이상과 비슷합니다. 완전한 제어, 예측 가능성, 컴파일러의 개입 없이 작업하고 싶어 합니다. Mojo는 이를 제공합니다. 타입을 제거하면 완전히 동적인 방식으로 작업할 수 있습니다.
진행자: 무언가를 int로 선언하면 JavaScript 세계에서 온 외부 데이터를 PureScript에서 문자열로 선언하는 것과 같습니다. 이제 문자열처럼 취급할 것입니다. 내부적으로는 거의 무엇이든 될 수 있습니다.
크리스 랫너: 저희는 TypeScript나 Python처럼 기본적으로 타입이 없지만 타입 힌트를 제공할 수 있는 언어와는 다릅니다. 타입 힌트는 오류 메시지 등에 사용할 수 있습니다. 저희는 그런 방식을 사용하지 않습니다. 저희는 타입과 PythonObject를 가지고 있습니다. PythonObject에서 int로 변환하려면 변환 작업을 수행해야 합니다. 변환 작업은 검사됩니다. "괜찮을 거야"라는 식의 타이핑은 없습니다. 실제로 타입이 지정됩니다. 이는 매우 중요합니다. 타입 시스템을 통해 모든 것이 올바르게 구성됩니다.
크리스 랫너: 이것이 가장 중요한 부분입니다. 스택 타입 시스템 내부에서 어떻게 작동하는지 살펴보면, 어떤 면에서는 매우 지루하지만, 다른 면에서는 매우 혁신적입니다. 지루한 면은 Rust의 트레잇, Swift의 프로토콜 등과 같은 현대 정적 타입 제네릭 고차 함수형 타입 시스템을 사용한다는 것입니다. 많은 현대 언어에서 사용하는 방식이며, 익숙한 방식입니다. 익숙함은 좋습니다.
크리스 랫너: 혁신적인 면은 핵심 기능만 언어에 포함하고, 나머지는 라이브러리에 위임한다는 것입니다. Rust나 C++는 강력한 언어이지만, 역사적인 이유로 언어 자체에 이상한 기능들이 많이 포함되어 있습니다. 예를 들어, C++에서 복소수는 std::complex 템플릿이지만, float과 double은 언어에 내장되어 있습니다. 내장 타입에만 적용되는 특정 변환, 특정 이상한 동작들이 있습니다. 예를 들어, C++에서는 연산자 .을 오버로드할 수 없고, 스마트 참조를 만들 수 없습니다.
크리스 랫너: Mojo에서는 가능한 모든 것을 라이브러리에 위임하고, 컴파일러에서 마법을 제거하는 적극적인 접근 방식을 취합니다. int, float 등은 언어에 내장되어 있지 않고, 모두 라이브러리입니다. 모두 구조체입니다. 타입 시스템이 구성되고, 모든 내장 타입에 사용됩니다. 배열 등도 모두 라이브러리로 작성됩니다. 이를 통해 언어는 라이브러리를 표현하는 데 필요한 핵심 기능만 제공하면 됩니다. Python 사용자를 위해 모든 것을 인체 공학적이고, 동적이며, 유연하고, 강력하게 만들고 싶습니다.
진행자: Haskell에서 처음 배웠을 때 불리언이 언어의 핵심 부분이 아니라는 사실을 알고 놀랐습니다. 불리언을 정의할 수 있는 도구만 제공합니다.
크리스 랫너: 그렇지 않으면 True의 별칭일 뿐입니다.
진행자: 그렇다면 실제로 어떻게 작동하는지 궁금합니다. 모든 주요 타입이 사용자 또는 라이브러리 공간에서 정의되고, int 배열에 대해 매우 고성능의 최적화를 원한다면 어떻게 될까요?
크리스 랫너: Mojo는 제로 비용 추상화를 믿습니다. 제로 비용 추상화는 C++, Rust 등 많은 언어에서 볼 수 있는 개념입니다. Mojo는 MLIR 컴파일러 기술을 활용하여 혁신적인 방식으로 구현하지만, 개념은 동일합니다. 이를 통해 추상화를 쌓을 수 있습니다. 하지만 추상화의 맨 아래에는 무언가가 있어야 합니다.
크리스 랫너: Mojo는 설계상 Swift의 교훈을 바탕으로 10배 더 발전시켰습니다. Swift에서 저는 때때로 Swift가 LLVM을 위한 문법적 설탕이라고 농담하곤 했습니다. Swift의 int도 라이브러리에서 정의되었고, LLVM I32 또는 I64의 래퍼였습니다. Swift는 최하위 레벨에서 LLVM 프리미티브와 직접 통신할 수 있었습니다. 즉, 문법적 설탕을 만들고, 타입을 정의하고, 연산자를 오버로딩하고, 이러한 모든 작업을 통해 핵심 연산을 구축하는 것입니다. Mojo는 기본적으로 동일한 트릭을 사용하지만, MLIR을 통해 더욱 강력해졌습니다. MLIR은 훨씬 더 현대적인 컴파일러 스택이므로 훨씬 더 강력한 기능을 제공합니다. float4와 같은 것을 노출하고 가속기에서 매우 이상한 숫자 계산을 할 수 있습니다. 텐서 코어와 같은 매우 이국적인 기능과 통신하고, 이를 멋진 라이브러리로 래핑할 수 있습니다.
크리스 랫너: 따라서 하드웨어의 성능과 매우 복잡하고 이국적인 것들에 대한 직접적인 저수준 액세스의 이점을 얻을 수 있습니다. 하지만 라이브러리에 내장된 문법적 설탕이 있으므로 컴파일러 전문가가 아니어도 시스템과 언어를 확장할 수 있습니다. 이는 매우 중요하다고 생각합니다.
진행자: 실제로 어떻게 보이는지 궁금합니다. 복소수 라이브러리를 Mojo로 작성한다고 가정해 보겠습니다. Python으로 작성하기 때문에 간단합니다.
크리스 랫너: 두 개의 float을 가진 구조체를 만들고, add 메서드를 오버로드하면 됩니다. 이는 Python에서 연산자 +를 오버로드하는 방식입니다.
진행자: 하지만 어느 시점에서 MLIR 코드를 조금 섞으면 이를 조정할 수 있을까요?
크리스 랫너: 원한다면 가능합니다. 예를 들어, 복소수를 사용해 보겠습니다. 복소수는 특정 칩에서 하드웨어 가속을 지원합니다. 단순히 두 개의 float을 더하는 것 이상입니다. 곱셈-누적 연산이나 복소수 곱셈을 수행하면 (수학이 가물가물하지만) 4번의 곱셈과 몇 번의 덧셈이 필요합니다. 특정 CPU에는 이러한 작업을 한 번에 수행하는 연산이 있습니다. 따라서 효과적으로 ifdef와 같이 컴파일 타임 메타 프로그래밍 시스템을 사용하여 이를 수행할 수 있습니다. 하지만 효과적으로 ifdef와 같이, 이 시스템에 이 기능이 있다면 최적화된 작업을 수행하고, 그렇지 않으면 일반적인 작업을 수행합니다.
크리스 랫너: 완전한 액세스 권한이 있습니다. 따라서 라이브러리 작성자는 하드웨어 수준 가속을 선택적으로 사용할 수 있습니다.
진행자: 멋진 대안을 가지고 있으면서도 클라이언트는 걱정할 필요가 없습니다. 정말 강력한 기능입니다.
크리스 랫너: 특정 아키텍처에서는 이 작업을 수행하고, 다른 아키텍처에서는 저 작업을 수행하고, 실패하면 Python 기능을 사용하는 것과 같은 작업을 수행할 수 있을까요?
크리스 랫너: 네. Mojo는 특별한 기능을 가지고 있습니다. 하나의 소스 코드를 작성하면 CPU와 GPU에서 부분적으로 실행할 수 있습니다. CPU와 GPU는 포인터 크기와 숫자 기능이 다를 수 있습니다. 전체 스택은 코드 크기 조정을 지원합니다. 이는 매우 심오하고 근본적인 컴파일러 기술 덕분에 가능합니다.
진행자: 누군가 유용한 라이브러리를 작성했지만 속도가 충분히 빠르지 않을 때, 프로그래밍 언어를 변경하지 않고도 하드웨어 최적화를 PR로 제출할 수 있는 미래를 상상할 수 있습니다.
크리스 랫너: 네, 그리고 Mojo는 2000년이나 1980년이 아닌 2022년에 설계되었습니다. SIMD와 같은 기능이 있습니다. SIMD 벡터는 모든 컴퓨터에 있으며, Mojo는 이를 직접 지원합니다. 명시적 벡터화도 직접 지원합니다. 따라서 하드웨어에 대한 완전한 액세스 권한을 얻을 수 있습니다.
크리스 랫너: 전 세계의 Mojo 개발자들이 라이프 게임과 같은 것을 예로 들어, 이 기능을 사용하여 1000배 더 빠른 코드를 작성하는 것을 보는 것은 정말 즐거운 일입니다. 라이브러리 기반 확장성 덕분에 벡터화된 함수는 단순한 라이브러리 함수입니다. 따라서 라이브러리의 조합기를 사용하여 코드를 벡터화할 수 있습니다. 많은 사람들이 이러한 성장 과정을 거치면서 Mojo에 대해 배우고, 흥미로운 것을 만들고, 새로운 것을 배우는 것을 즐기고 있습니다.
진행자: 그렇다면 이제 CPU와 GPU에 대해 자세히 알아보겠습니다. MLIR 공간에서 증명한 이론은 Mojo를 매력적으로 만드는 요소는 무엇인가요?
크리스 랫너: 언어적인 부분은 제외하고 직관적으로 설명해 드리겠습니다. AI 분야에서는 사용자 애플리케이션과 이를 지원하는 시스템 및 기술 모두가 중요합니다. 오늘날 Google Cloud에서 Jupyter Notebook에 가입하고 몇 줄의 코드만으로 엑사플롭스 슈퍼컴퓨터를 프로그래밍할 수 있다는 사실은 매우 놀랍습니다. 새로운 계산을 설명하면 수천 개의 칩에 걸쳐 매핑, 분할, 확장되어 대규모 데이터 센터 속도로 실행됩니다. 이는 HPC 분야에서 오랫동안 해왔던 일이지만, 이제 AI 연구자들도 이를 수행하고 있습니다. MPI 코드나 저수준 고성능 코드를 작성하지 않고도 가능합니다.
크리스 랫너: 이를 가능하게 한 것은 완전히 명령형 프로그래밍에서 선언형 프로그래밍으로의 전환입니다. AI에서는 머신러닝 그래프를 구축합니다. 여러 가지 변형이 있지만, 예를 들어 머신러닝 그래프를 구축한다고 가정해 보겠습니다. AI 연구자들은 행렬 곱셈, 컨볼루션, 게더링 감소 등과 같은 연산에 대해 생각합니다. APL과 비슷하게 생각합니다. 이러한 고도로 병렬화된 연산자의 간단한 구성에 대해 생각합니다.
크리스 랫너: 그런 다음 이 그래프를 매우 정교한 컴파일러 스택에 제공합니다. 컴파일러 스택은 각 연산자를 구현하는 함수를 선택할 뿐만 아니라 루프 네스트의 퓨전도 수행합니다. 매우 복잡한 수학을 매우 고급 컴파일러 변환을 통해 처리하고, 클러스터 전체에 분산합니다. 이러한 작업은 선언적 명세 덕분에 가능합니다. Python이나 C++ 코드를 병렬화하려면 많은 어려움이 따릅니다. 하지만 "4대의 머신에서 이 코드의 4개 복사본을 실행"과 같이 선언적으로 지정하면 상대적으로 쉽게 수행할 수 있습니다.
크리스 랫너: Mojo의 경우, 이 전체 스택은 Max 엔진으로 발전했습니다. Max 엔진은 매우 정교한 AI 컴파일러 스택입니다. XLA와 유사하지만, 많은 교훈을 얻은 후 개발되었습니다. 머신러닝 그래프를 실행할 수 있지만, 명령형 코드와도 통신할 수 있기를 원합니다. 따라서 사용자 정의 연산자를 작성하고, 새로운 알고리즘을 개발할 수 있어야 합니다. Modular에서 FFT를 모르지만 사용자가 알고 있고, 신호 처리 도메인에 매우 중요하다면 FFT를 제공할 수 있어야 합니다. 스택은 완전히 확장 가능해야 합니다.
크리스 랫너: Mojo는 사용자가 매우 간단하고 익숙한 코드로 알고리즘을 작성할 수 있도록 합니다. Python 문법을 사용하기 때문에 이해하기 쉽습니다. CUDA는 Python과 매우 다릅니다. CUDA 커널을 작성하는 대신 Mojo 코드를 작성할 수 있으며, 그래프 컴파일러 등이 이를 처리할 수 있습니다. MLIR 덕분에 코드를 분석하고 무슨 일이 일어나고 있는지 파악할 수 있습니다.
크리스 랫너: 이를 통해 정교한 컴파일러 퓨전, 배치, 라우팅 등을 수행할 수 있습니다. 이는 현재 다른 언어에서는 불가능한 기능입니다. AI 분야의 최첨단 기술은 CUDA와 Intel MKL과 같은 수학 라이브러리를 기반으로 구축되었습니다. 이러한 연산자는 모두 블랙박스입니다. 정교한 그래프 컴파일러가 있지만, 실제로는 조정하는 로직을 파악할 수 없기 때문에 고수준 변환을 수행할 수 없고, 여러 면에서 불안정합니다.
크리스 랫너: Mojo의 목표 중 하나는 이러한 문제를 해결하고, 모든 시스템을 한 단계 발전시키는 것입니다. 따라서 Mojo는 매우 높은 성능을 제공해야 합니다. 행렬 곱셈을 수행하는 벤더 라이브러리와 비교하여 최첨단 성능을 제공하고자 합니다. 또한 사용성도 중요합니다. 동일한 도구를 사용하여 그래프를 구축하는 사람들은 Python에 익숙합니다. PyTorch와 같은 환경을 제공하고 싶습니다.
크리스 랫너: 데이터 그래프를 구축하는 세계와 그래프에 삽입할 사용자 정의 노드를 만드는 세계 사이에는 분명한 경계가 있습니다.
크리스 랫너: 전통적인 TensorFlow와 PyTorch는 8~10년 전에 설계되었습니다. 연구 환경, AI 훈련 환경에서 개발되었습니다. 하지만 오늘날 AI 산업의 초점은 배포로 옮겨갔습니다. 배포 모드에서는 서버에서 AI 모델을 배포하고 실행할 때 Python을 사용하는 것이 적합하지 않을 수 있습니다.
크리스 랫너: 따라서 Python을 좋아하고 Python으로 작업하는 연구자들과 모델을 다시 작성하고 C++ 또는 Rust로 토큰화 로직을 다시 작성해야 하는 프로덕션 담당자들 사이에 격차가 발생합니다. Mojo는 이러한 문제를 해결하고자 합니다. 확장 가능한 하나의 언어를 제공함으로써 고성능 수치 전문가, 배포 엔지니어, AI 연구자 등 모든 사람들이 서로 소통할 수 있도록 합니다.
진행자: AI 세계가 그렇게 여러 분야로 나뉘어 있다는 사실을 몰랐습니다.
크리스 랫너: 네, 그렇습니다. 이는 매우 빠르게 발전하는 분야이기 때문에 기술을 처음부터 다시 검토할 기회가 없었기 때문입니다. 컴퓨팅 분야에서 8년은 매우 짧은 시간입니다. 모든 것이 매우 빠르게 발전했습니다. 따라서 프로그래밍 언어를 만드는 것은 매우 어려운 일입니다. 수년이 걸리는 프로젝트입니다. 따라서 이점과 비용, 이점과 비용을 신중하게 고려해야 합니다. 달성할 수 없는 목표를 설정해서는 안 됩니다.
진행자: 언어를 만들 만한 충분한 이유가 있다면, 저는 그것이 미친 짓이라고 생각하지 않습니다.
크리스 랫너: 취미 프로젝트로 언어를 만드는 것도 멋진 일입니다. 하지만 그 중간 어딘가에는 분명히 미친 짓이 있습니다.
진행자: 머신러닝 그래프 내에서 이러한 연산자 중 하나를 작성하는 것이 Python과 유사한 Mojo 프로그래머 공간에서 어떤 느낌일지 궁금합니다. 값과 값의 가중치를 생성하는 파이프라인과 비슷할 것 같습니다.
크리스 랫너: 어떤 종류의 코드를 작성하느냐에 따라 다릅니다. 하지만 Python 문법을 사용하는 C++ 또는 Rust 코드를 작성하는 것과 비슷하다고 생각하면 됩니다. 템플릿과 불필요한 코드를 제거하고 for 루프를 작성합니다. Mojo는 고수준 함수와 라이브러리에 내장된 조합기를 제공하므로 "이 영역을 병렬화하고, 이 영역을 벡터화"와 같은 작업을 구성하는 경우가 많습니다. 따라서 코드 스타일은 단순히 for 루프를 작성하는 것과는 조금 다릅니다.
크리스 랫너: Mojo는 사람들에게 혼란스러울 수 있습니다. Python에서는 for 루프를 작성하지 말라는 것이 일반적인 조언이기 때문입니다. Python은 느립니다. Python에서 for 루프를 작성하지 마세요. 하지만 Mojo에서는 이러한 조언이 적용되지 않습니다. Mojo는 Python과 동일한 구현이 아니기 때문입니다. 따라서 사람들이 알고 있던 많은 것들이 Mojo에서는 사실이 아닙니다. 고성능 텐서 코어와 VNNI 저수준 작업을 작성하는 전문가들이 Python으로 어셈블리 코드를 작성하는 것 같다고 말하는 것을 듣는 것은 매우 재미있습니다.
진행자: 정말 이상하네요.
크리스 랫너: 뇌를 뒤틀거나, 눈을 뜨게 하거나, 관점을 바꾸는 경험입니다. 하지만 그 외에는 익숙합니다. 저희가 추구하는 것은 새로움을 위한 새로움이 아니라 실용주의입니다.
진행자: 컴파일 타임 메타 프로그래밍에 대해서도 이야기해 보고 싶습니다.
크리스 랫너: 네, 좋습니다. 이는 저희가 한 또 다른 큰 도전입니다. AI 분야에서는 모델을 구축할 때 모델과 소스 코드가 효과적으로 메타 프로그램입니다. 명령형 로직의 집합이며, 그래프를 설명합니다. 그래프는 분산하고 변환하고 조작하는 대상입니다. Python은 오랫동안 다양한 분야에서 메타 프로그래밍에 사용되어 왔습니다. 이는 Python이 AI 커뮤니티에서 큰 성공을 거둔 이유 중 하나입니다.
크리스 랫너: 고성능 수치 연산 분야에서는 C++와 템플릿을 사용하는 경우가 많습니다. 부동 소수점과 배정밀도 부동 소수점 모두에서 작동하는 알고리즘을 원하기 때문에 템플릿을 사용하여 메타 프로그래밍을 합니다. 물론 이는 템플릿의 엄청난 혼란으로 이어질 수 있습니다. 최근에는 Zig와 같이 메타 언어와 언어를 분리하지 않고 동일한 언어를 사용하는 언어도 등장했습니다.
크리스 랫너: Mojo는 다른 분야의 훌륭한 작업에서 배우고 있습니다. Python은 매우 동적이며, 연산자를 오버로드하고, 동적으로 다양한 작업을 수행할 수 있습니다. 하지만 저희 분야에서는 성능 저하를 감수할 수 없습니다. 베어 메탈 성능을 제공하면서도 Python이 제공하는 추상화와 확장성의 이점을 누리고 싶습니다. 따라서 Python의 장점을 활용하고, 컴파일 타임 메타 프로그래밍을 도입하고, 이를 결합했습니다.
크리스 랫너: 이는 Mojo가 매우 표현력이 뛰어난 이유 중 하나입니다. Zig와 마찬가지로 표준 런타임 알고리즘을 구축하고, 힙 데이터 구조를 할당하고, 모든 작업을 수행한 다음 컴파일 타임에 사용할 수 있습니다. 따라서 C++ 템플릿과 같은 복잡한 코드를 작성하지 않고도 표현력이 뛰어난 라이브러리를 구축할 수 있습니다.
진행자: 컴파일 타임에 Python과 유사한 코드를 작성하여 Python 코드를 생성하고, 이를 컴파일하여 실행할 수 있다는 말씀이신가요?
크리스 랫너: 네, 그렇습니다. 또는 값, 객체, 함수, 기능, 클래스, 타입 등을 컴파일 타임이나 런타임에 사용할 수 있다고 말할 수도 있습니다.
진행자: 컴파일 타임에 자체 추상 구문 트리를 구성할 수 있나요?
크리스 랫너: 아직 그 단계까지는 도달하지 못했습니다. 하지만 원칙적으로는 가능합니다. 간단한 예를 들어 보겠습니다. 조회 테이블을 생성하는 함수가 있습니다. 일반적인 함수이며, 런타임에 호출할 수 있고, 동적 값을 인수로 전달할 수 있습니다.
크리스 랫너: 동적 값으로 채워진 동적 컬렉션을 제공해 달라고 요청합니다. 조회 테이블이 정적이고, 테이블에 대한 입력이 항상 정적이라고 가정해 보겠습니다. 런타임에 이 함수를 호출하고 테이블을 제공해 달라고 요청합니다. 컴파일 타임에 함수를 실행하고, 동적 데이터 구조를 계산하고, 모든 로직을 수행합니다. 출력은 객체이며, 실행 파일에 포함됩니다. 이제 객체를 직접 사용할 수 있습니다.
크리스 랫너: 이는 간단한 예입니다. 훨씬 더 복잡한 예도 많습니다. 타입 시스템 전문가라면 타입이 값이라는 것을 알고 있을 것입니다. 따라서 타입은 컴파일 타임 값입니다. 따라서 타입을 값으로 사용하여 컴파일 타임에 훨씬 더 복잡하고 고수준의 프로그래밍을 수행할 수 있습니다.
진행자: Zig에서 제네릭 리스트를 가져와서 컴파일 타임에 최적화된 버전을 만드는 방법과 비슷하네요.
크리스 랫너: 네, 같은 개념입니다. 물론 Zig는 고유한 특징을 가지고 있습니다. 매우 저수준 언어이며, 문법적 설탕과 같은 기능을 제공합니다. 따라서 Mojo와는 매우 다릅니다. Mojo는 라이브러리와 추상화를 가능하게 하는 데 중점을 둡니다. 하지만 컴파일 타임을 활용한다는 아이디어는 매우 일반적입니다. 저희는 다른 사람들의 훌륭한 작업에서 배우고 있다는 것을 인정합니다. 모든 것을 저희가 발명한 것은 아닙니다.
진행자: 우리 모두 거인의 어깨 위에 서 있는 셈이죠.
크리스 랫너: 네, 맞습니다. 그리고 저는 그들이 Lisp에서 영감을 얻었다고 확신합니다. Lisp는 모든 것의 시작입니다.
진행자: 모든 사람은 결국 자신의 Lisp를 구현하게 되어 있습니다.
크리스 랫너: 네, 그렇습니다. 하지만 그것은 미친 짓이 아닙니다. 좋은 학습 경험입니다. 배포가 항상 좋은 생각은 아닐 수도 있습니다.
진행자: CPU와 관련된 작업을 할 때 메모리 관리 문제가 발생할 수 있습니다. 이 문제를 어떻게 해결했는지 설명해 주시겠습니까?
크리스 랫너: PythonObject를 다룰 때는 CPython 객체 모델을 그대로 유지하는 방식을 사용합니다. 따라서 모든 것이 호환됩니다. Python을 가져오면 전통적인 참조 카운팅 방식의 간접 객체 박싱이 적용됩니다.
크리스 랫너: 실제 Mojo 코드 또는 네이티브 Mojo 코드를 작성하려면 매우 강력한 타입 시스템을 사용할 수 있습니다. 이동 생성자, 복사 생성자, 소멸자를 가진 타입을 사용할 수 있습니다. 따라서 수동으로 리소스를 관리하는 코드를 직접 작성할 수 있습니다. 이는 기본적인 기능 중 하나입니다. 안전하지 않은 후크를 통해 C를 호출하고, malloc과 free를 사용할 수 있습니다. 하지만 저희는 사람들이 라이브러리를 구성하고 안전한 방식으로 작업하기를 원합니다. 따라서 저희는 참조를 제공합니다.
크리스 랫너: Mojo의 참조는 Rust의 참조와 매우 유사하게 작동합니다. 구현 방식에는 차이가 있지만, 개념적으로는 동일하다고 생각할 수 있습니다. Mojo에서는 훨씬 덜 눈에 띄고, 빌림 검사기를 Rust만큼 세밀하게 관리할 필요는 없습니다. 하지만 참조를 관리할 수 있는 기능과 안전성을 제공합니다. 이는 매우 강력하고 중요한 기능입니다.
크리스 랫너: 저희는 Rust에서 많은 것을 배웠습니다. Rust는 훌륭한 언어이며, 많은 길을 개척했습니다. 하지만 빌림 검사기에는 몇 가지 어려움이 있습니다. Rust의 빌림 검사기는 파서에서 작동하며, 파서는 CIR을 생성하는 데 사용되는 복잡한 규칙과 특수 케이스를 가지고 있습니다. 빌림 검사기는 코드가 올바르게 작성되었는지 검사하고, 잘못된 경우 오류 메시지를 표시합니다. 간단한 경우에는 이해하기 쉽지만, 복잡한 경우에는 파서가 의도한 대로 작동하지 않을 수 있습니다.
크리스 랫너: Mojo에서는 빌림 검사기가 단순히 검사하는 역할만 하는 것이 아닙니다. 값의 수명을 결정합니다. Rust와 Mojo의 가장 큰 차이점은 Rust에서는 값이 범위가 끝날 때 파괴되는 반면, Mojo에서는 값이 마지막으로 사용된 직후 파괴된다는 것입니다. 마치 매우 강박적인 가비지 컬렉터가 실행되는 것과 같습니다.
크리스 랫너: 이는 몇 가지 의미를 가집니다. 첫째, 수명이 빨리 끝나기 때문에 배타성 위반이 훨씬 덜 발생합니다. 둘째, 메모리 사용량이 줄어듭니다. 예를 들어, GPU와 통신할 때 텐서가 4GB의 데이터를 보유하고 있다면, 이는 매우 중요한 문제입니다. 셋째, 꼬리 호출과 같은 핵심 프로그래밍 언어 기능에 더 적합합니다. 스택에 있는 객체가 꼬리 호출 후에 파괴되면 실제로는 꼬리 호출이 아닙니다.
크리스 랫너: Rust에는 드롭 검사 플래그라는 것이 있습니다. Rust와 Swift에서는 = 연산자가 값에 대한 재할당(변경)이거나 값의 첫 번째 초기화일 수 있습니다. Rust에서는 슬롯에서 값을 이동할 수 있기 때문에 이는 중요한 문제입니다. Mojo에서는 이 문제를 해결했습니다. 따라서 언어와 프로그래밍 모델이 단순화되고, 안전성과 참조의 표현성을 유지합니다.
진행자: 이는 컴파일 타임에 계산되는 건가요? 지금 말씀하신 내용은 참조 카운팅과 비슷하게 들립니다.
크리스 랫너: 네, 컴파일 타임에 계산되며, 수명을 사용합니다.
진행자: Rust의 수명에 대해 아는 것이라고는 사용하기 어렵다는 것뿐입니다. 적어도 저는 그렇게 생각합니다.
크리스 랫너: 어떤 사람들은 좋아하고, 어떤 사람들은 어려워합니다. Rust는 LLVM을 기반으로 구축되었으며, 저는 Rust 커뮤니티를 오랫동안 알고 있고 존경합니다. 하지만 Rust는 Swift와 비슷한 14년 정도 되었기 때문에 많은 것을 배웠습니다. Rust는 훌륭한 언어이며, 놀라운 커뮤니티를 가지고 있습니다. 하지만 Mojo는 학습을 통해 다음 단계로 나아갈 수 있는 기회를 제공합니다. 단순화할 수 있는 방법이 많이 있습니다.
진행자: Rust는 그런 의미에서 혁신적입니다.
크리스 랫너: 그렇습니다. Rust에서 배우는 것은 필연적입니다.
크리스 랫너: 저희는 Rust의 장점을 취하고 다음 단계로 나아가고 있습니다. Swift에서도 많은 실수를 저질렀습니다. 따라서 Mojo에서는 이러한 실수를 바탕으로 개선된 기능을 제공합니다. Mojo는 비동기-대기 기능을 기본적으로 지원합니다. 고성능 스레드 애플리케이션에 중요한 기능입니다. 고정(pinning)이 필요하지 않습니다. 이는 매우 중요한 차이점입니다. 모든 값이 고유한 ID를 가지고 있기 때문입니다.
크리스 랫너: 타입 시스템의 작동 방식에 대한 매우 간단하고 근본적인 저수준 변경 사항이 많이 있습니다. Rust 프로그램은 많은 memcpy를 수행합니다. memcpy를 제거하기 위한 최적화 기법이 있지만, 항상 효과적인 것은 아닙니다. Mojo에서는 이동으로 인해 암시적 memcpy가 발생하지 않습니다. 따라서 언어와 컴파일러가 구현되는 방식에 대한 많은 저수준 변경 사항이 있습니다.
진행자: ID라는 말씀을 듣고 불변 데이터에 대한 견해가 있는지 궁금합니다.
크리스 랫너: 아직 활발하게 논의 중입니다. Swift에서의 경험을 말씀드리자면, Swift는 함수형 프로그래밍을 적극적으로 도입했습니다. 함수형 프로그래밍은 데이터를 변경하지 않고 항상 새로운 값을 생성하기 때문에 구성 가능성, 예측 가능성, 제어 가능성 등의 이점을 제공합니다. 하지만 C++ 프로그래머들은 리스트에 항상 새로운 값을 삽입하고 새로운 리스트를 생성하는 것은 성능에 매우 좋지 않다고 주장합니다.
진행자: 마치 싸움에 휘말릴 것 같은 느낌이 드네요. 계속 말씀해 주세요.
크리스 랫너: Swift는 값의 배타적 소유권을 통해 값 의미론을 구현합니다. Swift의 값 의미론은 지역적 변경을 허용합니다. 따라서 Rust와 마찬가지로 값에 대한 배타적 액세스 권한이 있으면 값을 변경할 수 있습니다. Swift의 배열, 딕셔너리, 문자열 타입은 모두 Java와 같은 의미에서 불변입니다. 문자열을 가지고 있다면 그 값이 변경되지 않는다는 것을 알고 있습니다. 따라서 함수형 프로그래밍 관용구와 매우 유사합니다.
크리스 랫너: 문자열을 가지고 있다면 변경하지 않는 한 변경되지 않습니다. 변경하더라도 다른 코드에 영향을 미치지 않습니다. 구현 방식 덕분에 암시적으로 딥 카피가 발생하지 않습니다. Swift 생태계에서 개발되고 잘 작동하는 많은 기능들이 Mojo 생태계에도 자연스럽게 적용될 것이라고 생각합니다. 아직 작업 중입니다.
진행자: Python 프로그래머에게는 익숙하지 않은 방식으로 프로그래머 공간에 영향을 미칠 것 같습니다.
크리스 랫너: 하지만 이는 모두 선택 사항입니다. 완전히 동적인 방식을 사용하고 싶다면 얼마든지 그렇게 할 수 있습니다. Mojo는 기존 세계를 바꾸려는 것이 아니라 부족한 부분을 채우려는 것입니다. 현대 Python은 언어의 절반에 불과합니다. Python과 C가 있습니다.
진행자: 맞습니다.
크리스 랫너: Python으로 대규모 애플리케이션을 구축하려면 Python과 함께 C, C++, Rust 등을 사용하게 됩니다. 저희는 Python(적어도 문법)을 유지하면서 C를 대체하고 있습니다.
진행자: 아, 무슨 말씀인지 알겠습니다. 두 가지를 모두 수행할 수 있는 하나의 시스템을 구축하고 있습니다.
크리스 랫너: 따라서 Python과 C++의 FFI 및 바인딩과 같은 복잡한 작업 대신, __add__와 같은 기능을 동일한 방식으로 사용할 수 있습니다.
진행자: Mojo의 또 다른 중요한 주제인 스레드와 병렬 처리에 대해 이야기해 보겠습니다.
크리스 랫너: 네, Mojo에서는 스레드와 병렬 처리가 매우 중요합니다. 모든 최신 머신은 멀티 코어를 가지고 있습니다.
진행자: Python은 전역 인터프리터 잠금(GIL)으로 유명합니다. 스레드를 허용하면 spooky action at a distance 문제가 발생합니다. 무언가가 예기치 않게 변경됩니다. Mojo에는 이 문제를 해결할 수 있는 방법이 있다고 들었습니다. Mojo에서 병렬 처리를 어떻게 사용할 수 있나요? 병렬 프로그램을 어떻게 작성하나요?
크리스 랫너: Mojo에서는 라이브러리를 통해 병렬 처리를 지원합니다. 병렬 for 루프와 같은 기능을 제공합니다. 단순한 라이브러리 함수입니다. 병렬 for 루프에 함수를 전달할 수 있습니다. 이것이 가장 쉬운 방법입니다.
크리스 랫너: 매우 고성능의 저수준 스레딩 라이브러리도 제공합니다. 오늘날의 시스템은 단순히 4개 또는 8개의 코어를 가진 것이 아니라 256개의 코어를 가진 서버입니다.
진행자: 엄청난 숫자네요.
크리스 랫너: 앞으로 몇 년 후에는 더욱 엄청난 숫자가 될 것입니다. Mojo는 이러한 환경을 위해 설계되었습니다. 아직 액터 시스템을 구축하지는 않았습니다. Swift의 경우 완전한 액터 시스템을 구축했습니다. Swift의 액터 시스템은 타입 안전성을 제공하고, 대규모의 느슨하게 결합된 분산 에이전트에 매우 적합하며, 분산 액터도 지원합니다.
진행자: Swift가 그런 기능을 제공하는지 몰랐습니다.
크리스 랫너: 네, 그렇습니다. 매우 잘 작동합니다. 비동기-대기 기능을 기반으로 구축되었습니다. Mojo에도 액터 시스템을 도입할 수도 있습니다. 하지만 아직 우선순위는 아닙니다. 현재는 구조화된 계산, 슈퍼컴퓨터 스타일, 수치 연산 측면에 집중하고 있습니다. 언젠가는 액터 시스템을 구축하고 싶지만, 아직 우선순위는 아닙니다.
진행자: 누군가 라이브러리로 액터 시스템을 구축할 수도 있을까요?
크리스 랫너: 네, 가능합니다. Scala 세계에서는 Akka를 구축했고, 매우 성공적이었습니다. 단점은 메모리 안전성을 보장하지 않는다는 것입니다. Akka는 메모리 안전성을 제공하지 않기 때문에 몇 가지 어려움이 있습니다. 하지만 한 단계씩 진행하고 라이브러리로 구현 경험을 쌓는 것이 실용적일 수 있습니다. 컴파일러에 액터 간의 액세스를 중재하는 기능을 추가하는 것이 유익하다면, 타입 시스템 지원을 조금 추가할 수 있습니다.
진행자: 그렇다면 Mojo에서 병렬 처리를 위한 옵션은 비동기-대기, 스레드 생성, 병렬화된 프리미티브 등이 있겠네요.
크리스 랫너: 네, 그렇습니다. 원한다면 임의의 C 코드를 호출하고 원하는 모든 작업을 수행할 수 있습니다. 하지만 저희는 사람들이 그렇게 하지 않기를 권장합니다. 단순히 "이 작업을 병렬로 수행"과 같이 지정하는 것이 훨씬 편리합니다. 여전히 명령형 코드이지만, 더 선언적인 느낌을 줍니다. 저희는 추상화 수준을 높여서 사람들이 세부적인 구현에 신경 쓰지 않고도 작업할 수 있도록 하고 있습니다.
진행자: CPU 아키텍처가 Mojo 프로그래머에게 얼마나 중요한지 궁금합니다. CPU 아키텍처가 코드에 얼마나 영향을 미치나요? 얼마나 제어할 수 있나요?
크리스 랫너: 두 가지 측면이 있습니다. 얼마나 신경 써야 하는지, 그리고 얼마나 제어할 수 있는지입니다. 일반적으로 프로그래머는 "병렬 for 루프를 실행"과 같이 간단하게 지정하고 싶어 합니다. 시스템 수준에서는 구조화된 중첩 병렬 처리를 지원해야 하고, 스레드 라이브러리를 구성해야 하고, 비동기-대기를 사용해야 합니다. 수만 또는 수십만 개의 스레드가 생성되어 머신을 다운시키는 것을 방지해야 합니다.
크리스 랫너: 더 복잡한 가속기를 사용할 때는 더욱 중요해집니다. CPU는 256개의 코어를 가지고 있고, GPU는 수천 개의 코어 또는 수천 개의 스레드를 가지고 있습니다. GPU의 프로그래밍 모델은 전통적인 CPU 프로그래밍 모델과 매우 다릅니다. 저희의 목표 중 하나는 사람들이 훨씬 더 이식 가능한 알고리즘과 애플리케이션을 작성할 수 있도록 하는 것입니다.
크리스 랫너: 그래프 수준에서는 비교적 쉽습니다. 그래프를 이식 가능하게 만드는 방법은 간단합니다. 한 가지 하드웨어에 대한 그래프를 구현하고, 다른 하드웨어에 대한 그래프를 구현합니다. 선언적 방식의 장점은 구현 세부 사항을 분리하여 이를 가능하게 한다는 것입니다.
크리스 랫너: 하지만 for 루프를 작성할 때는 다릅니다. for 루프는 명령형 코드이고, 명령형 코드는 본질적으로 스택의 맨 아래에 있습니다. Mojo에서는 사용자가 Mojo에서 자체 추상화를 정의할 수 있도록 했습니다. 앞서 복소수 예제에서 살펴본 것처럼, 복소수는 매우 간단한 추상화이지만, 대상 하드웨어에 특정적인 해킹을 추가할 수 있습니다. 가속기에 대해 이야기할 때 가장 중요한 것은 병렬 처리와 메모리입니다. 특히 GPU와 LLM에서는 메모리 계층 구조를 효율적으로 사용하는 것이 가장 중요합니다.
크리스 랫너: 최신 GPU와 CPU는 다단계 메모리 계층 구조를 가지고 있습니다. CPU는 큰 벡터 레지스터 파일(L0 캐시), L1 캐시, L2 캐시, L3 캐시, 주 메모리 등을 가지고 있습니다. GPU도 기본적으로 동일한 구조를 가지고 있지만, 세부 사항은 다릅니다. 행렬 곱셈과 같은 작업을 수행할 때 고성능을 얻으려면 단순히 내적을 수행하는 것 이상이 필요합니다. 워크로드를 타일 단위로 처리해야 합니다.
진행자: 타일이라고 하셨는데, GPU를 이야기하고 있으므로 게임에서 맵에 사각형으로 표시되는 것을 상상하고 있습니다.
크리스 랫너: 텍스처를 생각하고 계시군요. 네, 텍스처는 2차원 데이터 사각형입니다. 하지만 AI에서는 이를 텐서라고 부르는 일반화된 개념을 사용합니다. 2차원 숫자 그룹을 n차원 숫자 그룹으로 만드는 것입니다. 같은 개념입니다.
크리스 랫너: 2차원 데이터 배열은 실제로는 메모리에 선형화됩니다.
진행자: 네, 그건 당연합니다.
크리스 랫너: 행을 따라 이동하면 포인터가 메모리의 다음 요소로 이동합니다. 하지만 열을 따라 이동하면 전체 행의 데이터를 건너뛰게 됩니다. 컴퓨터는 인접한 데이터에 액세스하는 것이 훨씬 쉽습니다. 건너뛰게 되면 효율성이 떨어집니다.
진행자: 멀티 차원 벡터에서는 이러한 문제가 더욱 심각해집니다.
크리스 랫너: 네, 그렇습니다. 행렬 곱셈을 수행할 때 두 개의 2차원 행렬을 고려해 보겠습니다. 일반적으로 한 행렬의 행을 가로로 이동하면서 다른 행렬의 행을 세로로 이동하여 출력 요소를 계산합니다. GPU에 두 행렬을 다르게 배열하라고 지시할 수 있다면, 예를 들어 미리 전치하면 훨씬 더 효율적일 수 있습니다. 한 번에 한 행과 한 열을 처리하는 대신, 두 행과 한 열을 처리할 수 있습니다. 그러면 열을 처리할 때 인접한 두 요소에 액세스하게 됩니다.
크리스 랫너: 이를 일반화하면 타일이라는 개념이 나옵니다. 2차원 메모리 블록을 처리하는 논리적 개념입니다. 최신 하드웨어는 복잡하고, 벡터화되어 있고, 병렬 처리를 지원하고, 행렬 연산을 실리콘에 추가하고 있습니다. 일반적으로 4x4 또는 16x16 크기의 작은 행렬의 행렬 곱셈을 수행할 수 있습니다. 일부 가속기는 128x128 크기의 행렬 곱셈을 수행할 수 있습니다.
크리스 랫너: AI는 중요하며, 실리콘은 기본적으로 2차원입니다. 따라서 실리콘의 2차원적 특성을 활용하여 행렬 곱셈을 구현하면 성능과 에너지 효율성을 높일 수 있습니다. 이 분야의 과제는 이러한 가속기를 사용하는 방법, 타일을 장치에 매핑하는 방법, 메모리 계층 구조를 효율적으로 사용하는 방법입니다. 이는 수치 연산만큼 중요하며, 성능 차이가 10배 또는 100배까지 날 수 있습니다.
크리스 랫너: 지난 10~20년 동안 HPC 분야에서는 이러한 문제를 해결하기 위해 노력해 왔습니다. Fortran, C++ 템플릿 라이브러리 등 다양한 솔루션이 개발되었습니다. 하지만 이러한 솔루션은 매우 특수한 경우에만 효과적이었고, 사용성이 좋지 않았습니다.
크리스 랫너: Mojo는 이러한 문제를 해결하기 위해 다양한 하드웨어 기능(예: 행렬 곱셈 연산), 고차 조합기, 라이브러리, 컴파일 타임 메타 프로그래밍을 지원합니다. 사용자는 타일 알고리즘만 작성하면 되고, 조정 로직은 신경 쓰지 않아도 됩니다. 컴파일 타임 메타 프로그래밍을 통해 재사용 가능하고 이식 가능한 코드를 작성할 수 있습니다.
크리스 랫너: 최근 NVIDIA GTC 컨퍼런스에서 Mojo가 GPU와 CPU에서 동일한 알고리즘에 대해 고성능 수치 연산을 수행할 수 있도록 하는 방법에 대한 발표를 했습니다. 이는 단순히 GPU와 CPU에서 소프트웨어를 실행하는 것만이 아니라, 소프트웨어 투자를 10년 또는 20년 동안 지속하고 새로운 하드웨어의 요구 사항에 맞게 소프트웨어를 조정할 수 있도록 하는 것입니다.
크리스 랫너: 하드웨어는 계속해서 발전하고 있으며, 그 어느 때보다 빠르게 발전하고 있습니다. Mojo는 이식 가능한 소프트웨어를 개발하는 데 방해가 되는 장벽을 허물고, 동시에 고성능의 최신 하드웨어 기능을 활용할 수 있도록 합니다.
진행자: 실제로는 어떻게 작동하나요? NVIDIA에서 새로운 GPU와 새로운 CPU 명령어를 출시한다고 가정해 보겠습니다. MLIR이 새로운 CPU 명령어를 지원할 때까지 기다렸다가 Mojo 라이브러리를 통해 사용할 수 있을까요?
크리스 랫너: NVIDIA는 소프트웨어 세계에서 훌륭한 시민입니다. 새로운 칩을 출시할 때마다 LLVM을 통해 액세스할 수 있도록 지원합니다. 따라서 Mojo는 MLIR과 LLVM을 통해 이러한 기능에 직접 액세스할 수 있습니다. 일반적으로 하드웨어 제조업체는 LLVM을 적극적으로 지원하고 있으며, 이는 혁신을 촉진하는 데 도움이 됩니다.
진행자: 멋지네요. 그리고 다시 한번 강조하지만, 이는 매우 소수의 사람들만 신경 쓰는 매우 전문적인 분야입니다. 대부분의 사람들은 고수준 라이브러리를 사용할 것입니다. Mojo는 Max 엔진과 같은 고수준 라이브러리를 제공합니다. Max 엔진은 그래프를 제공하기만 하면 됩니다. 세부적인 구현은 신경 쓰지 않아도 됩니다. 또는 Python 코드를 Mojo로 변환하여 100배 또는 1000배 더 빠르게 실행할 수 있습니다. 복잡한 가속기를 사용하지 않고도 CPU에서 실행할 수 있습니다. Python 코드에 많은 투자를 했고, 코드를 더 빠르게 만들고 싶지만, 엔지니어를 다시 교육하고 싶지 않다면 Mojo가 좋은 선택입니다.
진행자: 저는 GPU 수준의 코드를 작성하고 싶지는 않습니다. 단지 코드가 빠르게 실행되기를 바랍니다. 하지만 코드가 느리다는 것을 알게 되면, "잘못된 언어를 선택했습니다"라는 말을 듣지 않고도 코드를 개선할 수 있기를 바랍니다.
크리스 랫너: 맞습니다. 바로 그 점이 Python과 유사한 언어를 선택한 이유입니다. 프로그래머는 바쁩니다. 대부분의 사람들은 새로운 것을 배우기 위해 시간을 할애할 여유가 없습니다. 물론 예외는 있습니다. 새로운 것을 배우는 것을 좋아하는 연구자와 열정적인 사람들이 있습니다. 하지만 대부분의 사람들은 바쁩니다. 따라서 새로운 것을 배우는 데 1주일 또는 1개월을 투자할 시간이 없습니다.
크리스 랫너: 반면에 사람들에게 익숙한 것을 제공하고, 처음부터 다시 배울 필요가 없도록 하면, 사람들은 새로운 도구를 배우고 성장할 수 있습니다. Swift를 출시했을 때 Objective-C 개발자들에게 클래스는 여전히 Swift에서 작동하지만, 구조체라는 새로운 기능도 있다고 말했습니다. 구조체는 훨씬 더 효율적이고, 동적 디스패치를 사용하지 않습니다. 또한 대수 데이터 타입, 열거형, 패턴 매칭과 같은 새로운 기능도 도입했습니다. 이는 개발자들에게 큰 깨달음을 주었고, 점진적으로 새로운 것을 배울 수 있어서 좋아했습니다.
크리스 랫너: Mojo는 사람들에게 익숙한 환경을 제공하고, 처음부터 다시 배울 필요 없이 성장할 수 있도록 지원합니다. "Python 프로그래머입니까, 아니면 C++ 프로그래머입니까?"와 같은 질문에 답할 필요가 없습니다. 더 많은 사람들이 참여하고, 좋은 아이디어를 공유할 수 있다면, 컴퓨터 과학과 이러한 기술은 더욱 발전하고 더 큰 영향을 미칠 수 있을 것입니다. 저는 개발자를 믿습니다.
진행자: Mojo가 성공하기를 바랍니다. 프로그래머에게 새로운 아이디어를 제공하고, 프로그래밍에 대한 단편적인 시각을 통합하는 데 도움이 되는 모든 것을 지지합니다.
크리스 랫너: 네, 저도 그렇게 생각합니다.
진행자: Mojo를 설치하고 기존 웹 서버 작성 지식을 활용하여 Python 세계의 기존 웹 서버 라이브러리를 pip install하는 것과 동일한 작업을 수행할 수 있을까요?
크리스 랫너: 네, 가져와서 사용할 수 있습니다. 래퍼를 작성할 필요 없이 가져와서 사용하면 됩니다.
진행자: 정말 그렇게 작동하나요?
크리스 랫너: Python 소스 코드를 .mojo 파일로 옮기면 현재는 변경해야 할 부분이 있습니다. 현재 가장 부족한 기능은 클래스입니다. Python에서는 클래스가 매우 중요합니다. Mojo는 강력한 구조체, 강력한 정적 기능, 참조 등을 제공합니다. Mojo는 C++ 및 Rust와 동등한 기능을 제공합니다. 하지만 아직 부족한 부분이 있습니다.
크리스 랫너: 따라서 시스템 프로그래밍에 익숙한 개발자라면 Mojo를 편안하게 사용할 수 있을 것입니다. 6개월 후에는 클래스와 다른 기능이 추가될 예정입니다. 그러면 Python 프로그래머도 더 편안하게 사용할 수 있을 것입니다.
진행자: Mojo를 설치해 보고 얼마나 편안한지 확인해 보겠습니다.
크리스 랫너: 네, 좋습니다. Mojo는 매우 재미있는 언어입니다. 저는 이러한 작업을 좋아합니다. 오랫동안 저의 열정이었습니다. 하지만 저는 커뮤니티 측면도 좋아합니다. Swift의 경우, 아직도 길에서 사람들이 저를 알아보고 Swift를 개발해 주셔서 감사하다고 말합니다. 덕분에 프로그래밍을 배울 수 있었고, Objective-C는 너무 어려웠다고 합니다.
크리스 랫너: Mojo를 통해 Python을 알고 있지만 스스로를 진정한 코더라고 생각하지 않는 사람들이 성장할 수 있기를 바랍니다. "Python 프로그래머입니까, 아니면 C++ 프로그래머입니까?"와 같은 질문에 답할 필요가 없습니다. 더 많은 사람들이 참여하고, 좋은 아이디어를 공유할 수 있다면, 컴퓨터 과학과 이러한 기술은 더욱 발전하고 더 큰 영향을 미칠 수 있을 것입니다. 저는 개발자를 믿습니다.
진행자: Chris Latner, 인터뷰에 응해 주셔서 감사합니다.
크리스 랫너: 초대해 주셔서 감사합니다.
진행자: Python 프로그래머가 스스로를 진정한 프로그래머라고 생각하지 않는다는 것은 슬픈 일입니다. 물론 당신은 진정한 프로그래머입니다. 컴퓨터가 이전에는 할 수 없었던 일을 하도록 가르치는 일을 하고 있다면, 당신은 진정한 프로그래머입니다. 누가 뭐라고 하든 신경 쓰지 마세요. 더 나은 방법을 배우고 탐구하고 있다면, 당신은 저의 친구입니다.
진행자: Mojo에 대한 자세한 내용은 쇼 노트에서 확인할 수 있습니다. Mojo를 사용해 보고 싶다면 쇼 노트의 링크를 확인하세요. 저도 Mojo를 사용해 보고 있으며, 아직 초기 단계이지만 매우 유망하다고 생각합니다.
진행자: 좋아요, 공유, 평점, 구독을 클릭하여 피드백을 남겨 주세요. 여러분의 피드백은 큰 도움이 됩니다. 그러면 알고리즘이 비슷한 생각을 가진 다른 사람들에게 이 팟캐스트를 추천할 것입니다. 다음 에피소드가 나올 때까지, 이제 작별 인사를 드리겠습니다. 저는 진행자였습니다. Chris Latner와 함께한 개발자의 목소리였습니다. 들어주셔서 감사합니다.