나의 노트

Genie Sessions: Optionality

다음은 켄트 벡(Kent Beck)의 라이브 코딩 세션 녹취록의 한국어 번역입니다. 개발 전문 용어와 라이브 방송의 구어체 뉘앙스를 살려 번역했습니다.

좋아요. 이제 라이브가 시작된 것 같네요. 어디 봅시다. 화면을 좀 띄워야겠군요. 서브스택(Substack)을 봅시다. 지니 세션: 옵셔널리티(Optionality). 네. 제 거대한 화면에 띄워볼게요. 그래야 혹시라도 유용한 피드백이 있으면 볼 수 있으니까요. 뭐 별로 기대하진 않지만요. 어쨌든, 자 됐습니다.

지금 몇 분이 보고 계신지 안 보이네요. 피드백도 전혀 안 보이고요. 그러니까 뭘 할 거냐면요... 아, 화면 전환을 해야겠네요. 자, 전환합니다. 네, 잘 되고 있는 것 같네요. 또 다른 지니 세션(Genie session)에 오신 것을 환영합니다.

저는 '증강 개발(augmented development)'을 좀 해보려고 합니다. 지난주 세션의 연장선인데요, 그때는 기본적인 데이터 구조를 만들고 있었죠. 이번에는 거기에 GPU를 사용하고 있습니다. 지난번에 멈췄던 지점에서 제가 사실 혼자서 진도를 확 빼버렸는데요, 음, 다시 돌아가고 싶네요. 'Fairy table'을 가져올게요.

자, 여기가 우리가 멈췄던 곳입니다. 저는 계속 진행하고 싶어요. 어디 봅시다. 이 커밋 ID를 복사할게요. 그리고 지니(AI)에게 말할 겁니다. 아, 제 프롬프트 전체가 안 보이시죠? 제 얼굴을 화면 어디에 둬야 할까요? 위쪽으로? 거긴 말고. 여기. 제 얼굴을 여기 위로 올릴게요. 크기도 좀 줄이고요. 됐습니다.

자, 이제 제가 프롬프트에 뭐라고 치는지 보이시죠? "롤백해서 다시 개발을 시작하고 싶어. 커밋 ID는 이거고..." 붙여넣기 하고 시작합니다. 자, 이게 좀 이상할 겁니다. 유튜브 라이브, 유튜브 라이브... 지금 이만큼의 사람들이 보고 있다고 나오네요. 라이브 댓글도 달리고 있고요. 제가 뭔가 놓치고 있을 수도 있지만, 뭐 어때요. 강제할 순 없죠.

브랜치를 하나 생성합니다. 'optionality'라고 부르죠. 여기서부터 어떻게 되나 봅시다. 이게 바로 증강 개발의 아주 기초적인 기술 중 하나입니다. 작업을 싹 걷어내고 다시 하는 거죠. 이번엔 더 잘할 수 있을지도 모르니까요.

아, 혹시 이 동화(fairy tale) 안 보셨으면 읽어보시길 추천합니다. 이거 쓸 때 정말 재밌었어요. 쓸 때 혼자서 낄낄거리고 소리 지르고 그랬거든요. 누가 좀 듣고 있었으면 좋겠는데, 뭐 알게 되겠죠.

자, 지니 세션: 옵셔널리티. 제가 동시에 백만 가지 일을 하려고 해서요. 서브스택을 보고 있는데 라이브라고 뜨네요. 채팅창에 글을 남길게요. 스레드를 새로 시작합니다. "지금 여기서 라이브 중. 라이브 피드백은 여기에 댓글로 달아주세요." 이건 유료 구독자분들만을 위한 겁니다. 물론 나중에 유튜브에도 올라가겠지만, 여러분은 지금 여기서 저랑 바로 소통할 수 있으니까요. 큰 혜택이죠.

자, 동화 이야기를 하고 있었죠. 기본 데이터 구조인 '정렬된 맵(sorted map)'입니다. 네 가지 연산이 있어요. get(가져오기), put(넣기), delete(삭제하기), 그리고 이 키(key)와 저 키 사이를 iterate(순회)하는 거죠. 그게 정렬된 부분(sorted part)이고요. CPU 버전으로는 수없이 구현해 봤습니다. 여러 언어로 말이죠. 그런데 GPU는 완전히 다른 트레이드오프(trade-off)를 줍니다.

그래서 그걸 해보고 싶었어요. 기본 연산은 작동하게 만들었는데, 와우, 제가 모르는 GPU 관련 내용이 산더미더라고요. 여기서 증강 개발의 핵심 기술 하나가 나옵니다. 바로 '호기심'입니다. "좋아, 좋아, 이게 어떻게 작동하는 거지?" 이게 하나의 핵심 기술이고요. 또 다른 핵심 기술은 '놀이 감각(sense of play)'입니다. "좋아, 엉뚱한 아이디어네. 한번 해보자. 되돌릴 수 있다면(reversible), 뒤집을 수 있고 아무도 안 다치고 불법도 아니고 비도덕적인 것도 아니라면 그냥 해보는 거야." 짧은 시간을 투자해서 정말 멋진 걸 발견할 가능성이 있다면 그 비용은 아주 저렴한 셈이죠.

그래서 GPU가 어떻게 작동하는지 설명하는 동화를 써야 했어요. "WGPU의 땅에는 '슬랩(Slab)'이라는 조용한 왕국이 있었습니다. 슬랩은 완벽한 질서로 열쇠(key)들을 보관했고, 왕국의 크기를 항상 알고 있는 현명한 파수꾼 '메타(Meta)'가 지키고 있었습니다. 어느 날 여행자가 열쇠 목록을 가져와서 왕국에 그 값(value)들을 물었습니다." 제가 이것보다 더 잘 쓸 수도 있었지만 그럴 필요까진 없었죠. "여왕인 '우리 프로그램'은 여행을 위해 세 가지 선물을 준비했습니다. 전령들이 읽을 열쇠가 적힌 두루마리, 답을 적을 빈 장부(이게 결과 버퍼죠), 그리고 열쇠가 몇 개인지 알려주는 작은 쪽지였습니다. 여왕은 64명의 요정(sprite)을 소환했습니다. 각 요정은 열쇠 하나를 가지고 슬랩으로 달려가 신속한 이진 탐색을 수행했습니다. 열쇠를 찾으면 요정은 그 값을 장부에 새기고 찾았다고 표시했습니다. 없다면 페이지를 비워두었죠. 모든 요정이 돌아오면 장부는 경계(boundary)를 넘어 다시 옮겨졌고(이게 리드백 readback이죠), 여왕의 서기들은 그 표시를 여행자를 위한 답변 목록으로 번역했습니다." 이게 바로 대량 조회(bulk get)가 GPU 영역을 여행하는 과정입니다. 열쇠 두루마리, 요정 부대, 신속한 탐색, 그리고 결과 장부. 이제 좀 이해가 되네요. 아직 코드를 짤 수는 없지만 감은 잡았어요.

이제 소스 코드를 봅시다. 현재 모든 게 파일 하나에 들어있습니다. 그렇다고 계속 파일 하나에 두겠다는 건 아닙니다. 단지 지금은 그렇다는 거죠. 원칙은 "키우고 나서 쪼갠다(Grow then split)"입니다. 쪼갤 타이밍이 되기 전에는 나누지 않습니다. 키우고 나서 나누죠. 이제 많이 쪼개게 될 겁니다. 왜냐하면 우리는 "작동하게 하고(make it run), 올바르게 만들고(make it right), 빠르게 만드는(make it fast)" 순서에 있거든요. 확실히 작동하는 건 있으니, 이제 올바르게 만들어야죠.

여기 SortedMap이 있고 버퍼들이 잔뜩 있습니다. 그리고 get을 위한 컴퓨트 파이프라인(compute pipelines), get bind group... merge bind group은 어디 있죠? 여전히 제가 이해 못 하는 것들이 있네요. 괜찮습니다. 그리고 bin. 코드를 쭉 스크롤 하면서 훑어봅시다. 다 이해한다는 뜻은 아니지만 대충 뭐가 어떻게 돌아가는지 보는 거죠. new가 있어서 초기화를 하고요. bulk get. bulk get pipeline. bulk merge. bulk merge pipeline. get put.

아, 우리가 아직 delete를 구현 안 했나요? 스크롤을 내려보니 bulk put은 길고 복잡하네요. bulk get도 길고 복잡하고요. 아마 delete를 구현 안 했나 봅니다. 그러니 delete를 다시 구현해야겠네요. 좋아요. "Delete와 Bulk delete를 툼스톤(tombstone)을 저장하는 방식으로 구현해 줘. 테스트 코드도 작성하고 모든 코너 케이스(corner cases)를 다뤄줘."

조금 더 할 일이 있었네요. 괜찮습니다. 작업을 좀 날려먹었지만, 뭐 어때요. 작업 비용이 저렴하면 날려버릴 수도 있는 거죠. 어, 제 채팅에 답글이 달렸네요. "스트리밍 보시는 분들은 오른쪽 상단 아이콘을 클릭해서 라이브 채팅에 참여할 수 있습니다."라는데, 제가 그걸 어떻게 하죠? 라이브 채팅이 어디 있죠? 메탈 몽키(Metal Monkey)님. 웁스, 이름을 잘못 발음해서 정말 죄송합니다. 메탈 몽키님. 네, 어떻게 하는지 안 보이는데 괜찮습니다. 아시는 분 있으면 제 채팅에 댓글 남겨주시면 거기서부터 해보죠. 적어도 한 분은 보고 계신다는 걸 알았으니, 그 부분은 좋네요.

자, 여기 있습니다. "툼스톤 기반의 delete, bulk delete 추가, GPU bulk get 셰이더가 툼스톤을 '없음(missing)'으로 처리하도록 업데이트, 그리고 코너 케이스 테스트(기존 키 삭제, 섞인 키, 중복, 삭제 후 삽입, 빈 bulk delete 등) 커버." 테스트 돌려볼까요? 아냐, 굳이 테스트 돌리지 마세요. 제가 "바징가(Bazinga), 테스트 실행해"라고 말하면 그때 하세요. "만약 다 통과하면 성능 측정(perf)을 실행하고 결과를 저장해. 성능은 중요하니까. 그리고 커밋하고 푸시해. 바징가." 됐습니다.

이 정도만 해도, 지금 17명의 라이브 시청자를 돕고 있네요. 미디어 대기업이 되는 날도 머지않았습니다. 근데 이거 어디서 보는 거예요? 저는 안 보이는데 보고 싶네요. 하지만 괜찮습니다. 누군가 찾는 법을 알려주겠죠. 유튜브 라이브는 해봤는데 서브스택 라이브는 처음이라서요. 기꺼이 보고 싶은데 말이죠. 라이브 비디오를 클릭하면 되나? 재귀적인 지옥에 빠지는 거 아니에요? 좋아요. 이거 원해요. 제 얼굴 바로 위에 카운트가 있다고요? 제 화면엔 안 보이는데요. 뭐, 알겠습니다.

17명의 유료 구독자 여러분, 환영합니다. 함께해서 기뻐요. 오 세상에, 뭘 잔뜩 설치하고 있네요. 뭐 원래 그런 거죠. 적어도 제가 직접 설치 안 해도 되니까요. 어떻게 되나 봅시다. 계획은 앞으로 45분 정도 더 진행하는 겁니다. 이제 cargo test. 제발 실행돼라. 프로파일링, 외부 타입? 와우. 좋아요, 뭐 많이 하고 있네요. 올바른 걸 하고 있길 바랍니다. 45분. 질문 있으시면 채팅 스레드에 남겨주세요.

자, 성능(perf)을 돌릴 겁니다. 왜냐하면 CPU 대비 성능을 추적하는 게 중요하거든요. 언젠가는 진짜 거대한 엔비디아(NVIDIA) 머신을 구해서 진짜로 밀어붙일 때 어떻게 되나 볼 겁니다. 그건 나중 일이고요. "테스트 통과. 성능 측정 실행 및 저장 완료. optionality 브랜치에 커밋 및 푸시 완료." 됐습니다. 아니요. PR(Pull Request)은 안 해요. 그냥 여기다 커밋을 계속 쌓을 겁니다.

좋아요. 다음엔 뭘 할까요? 중복이 잔뜩 있습니다. bulk get, put. 이게 다... 자, bulk put은 이 복잡한 덩어리고, bulk get도 이 복잡한 덩어리입니다. 구조를 좀 더 살펴보죠. 이 세 가지 연산, bulk put, bulk get, bulk delete는 WGPU 관련 코드가 잔뜩 들어있고 서로 꽤 비슷해 보입니다. 스크롤 하면서 그냥 좀 보죠. 그나저나 텍스트 가독성은 괜찮나요? 예전에 불평하신 분들이 있어서요. bulk get을 보니, 쪼개기(split) 전에 꽤 많이 커진 것 같네요. 괜찮습니다.

마르코(Marco)님, 질문 감사합니다. 네, 괜찮아요. 만약 구조가 어때야 하는지 확실히, 정말 확실히 알고 있고, 지금 구조를 도입하는 게 지체되지 않는다면, 당장 도입하세요. 저는... 사실은 아니지만, GPU 프로그래밍을 20년, 25년 동안 안 했습니다. 그래서 구조가 어때야 하는지 모릅니다. 이 WGPU API는 너무 복잡해요. 디테일은 엄청 많은데 추상화(abstraction)가 없어요. 이건 순수하게 '덩어리파(lumper)' API입니다. 제가 '덩어리파(lumpers)'와 '분할파(splitters)', 그리고 'Tidy First'에 대해 얘기했었나요? 어쨌든, 덩어리파는 모든 걸 한눈에 볼 수 있게 큰 요소를 선호하고, 분할파는 작은 것들이 모여 해결책을 만드는 걸 좋아하죠. 이건 전형적인 덩어리파 API입니다. 모든 유연성을 갖추고 있죠. 하지만 보시다시피 제가 get 연산을 구현하려고 하니, 버퍼를 만들고... 결과 버퍼와 리드백 버퍼를 만들고... 이게 결과 버퍼랑 뭐가 다른지 아직 모르겠고... 메타 버퍼도 있고, 바인드 그룹(bind group)은 또 뭔지, 바인드 그룹 엔트리, 인코더, 파이프라인... 그리고 복사를 좀 하고 결과를 내놓고... 이 안 어딘가에서 실제로 GPU 코드를 실행하는데 어디에 있는지 전혀 명확하지 않습니다. "세 가지 bulk 연산에 중복이 많아. 각각을 위한 구조체(struct)를 만들어." 객체 지향 언어라면 객체를 썼겠지만 괜찮아요. "공통점을 캡처하는 구조체를 만들어."

마르코님, "언제 이런 변경을 해야 하는가?"라고 물으셨죠. 그게 바로 설계 기술입니다. 그나저나 라이브 채팅에서 누가 재밌는 얘기 하면 저 좀 알려주세요. 라이브 채팅 들어가는 법 아시는 분도 좀 알려주시고요. 지금도 꽤 산만하지만 더 산만해지면 좋겠네요. 제가 여기는 욕설이 허용되는 채널(cussing channel)이라고 말했나요?

오, 마르코님, 물론이죠. 자, 마르코님, 질문입니다. GPU 작업도 안 해봤고 Rust도 모른다면 구조적인 결정을 얼마나 하고 싶으신가요? 가능한 한 적게 해야죠. 일단 뭔가 작동하게 만듭시다. 이게 고전적인 "일단 돌리고(Make it run), 올바르게 고쳐라(Make it right)"의 차이입니다. 일단 작동하게 두고, 그다음에 우리가 가졌어야 할 구조에 대해 이야기합시다. 이 모든 엔트리와 블라블라가 있는 WGPU API를 보면, "와, 여기에 누락된 추상화가 있네. 100% 누락됐어."라는 생각이 듭니다. 하지만 그게 뭔지 아직 몰라요. 그래도 일반화할 수 있는 예제들이 좀 있습니다.

Bulk merge pipeline. 좋아요. "각각의 구조체로 위임해(Delegate). 유니폼 버퍼(uniform buffer) 생성을 중앙화하는 create uniform buffer 추가." 오, 고마워라. "BulkPutOp, BulkGetOp, BulkDeleteOp 구조체 추가. 연산별 상태와 실행을 보유." 좋아요. "세 가지 bulk 연산을 전용 구조체로 리팩터링해서 공유되는 설정, 디스패치(dispatch), 리드백 로직을 연산별로 캡슐화하고, 상용구(boilerplate)를 줄이기 위해 작은 유니폼 버퍼 헬퍼를 사용." 좋아요. 네. 바징가. 웁스. 지니를 쓰면 오타 걱정을 덜 해도 돼서 좋아요. 제 컴파일러는 이렇게 관대했던 적이 없거든요. 어제는 뭘 하고 있었냐면, 아이폰에 연락처를 추가하는데 이름, 성, 전화번호가 있잖아요. 텍스트를 복사해서 "이 연락처 추가해"라고 하고 싶었는데, 익숙해져 버린 거죠. 대신 이름을 복사해서 붙여넣고, 성을 복사해서... 아, 정말 미칠 노릇이었죠.

좋아요. 다 된 것 같네요. 이제 Bulk get bind group layout. 이건 구조체 안에 있어야 해요. 그게 요점이었죠. 어디 보자. 좋아요, 이제 ops가 op 구조체를 operations.rs 파일에 넣은 것 같네요. 우리가 이걸 하고 싶은 건 맞는데, 질문은 '언제 하고 싶은가'입니다. 지금 당장 하고 싶긴 한데 확신이 안 서네요. Bulk get op, Bulk put op. 누가 메시지를 보냈네요. 안녕, 사람. GPU sorted map. 여기 new가 있네요. 좀 더 간단해 보이는지 봅시다. 슬랩 크기, 슬랩 용량, 입력 버퍼 슬랩 메타데이터. 메타 버퍼, 병합 버퍼. 좋아요, put 연산의 경우... GPU에서 가능한 모든 연산을 수행합니다. 병합 버퍼 관련 내용인 것 같네요. Bulk get shader. 이건 get op에 속해야 해요. "다음 단계 계획해 줘." 어떻게 하나 봅시다. 아마 이 작업에 2시간은 쓸 것 같네요. 작동하게 만드는 데 1시간 썼고, 올바르게 만드는(make it right) 데 2시간은 쓸 겁니다. "뭘 옮기길 원해? 정렬(Sort)이랑 중복 제거(Deduplicate)? 그래." A. GPU 정렬 및 중복 제거(dedupe) 도입. B. 정렬, 중복 제거... 자, put이 작동하는 방식은, 슬랩(Slab) 자체가 있고, GPU에는 정렬된 항목들이 있습니다. 이건 bulk put이니까 새로운 항목들이 잔뜩 들어오겠죠. 이걸 병합(merge)할 수 있게 정렬해야 합니다. 그리고 중복을 제거해야죠. 만약 중복 항목이 있다면, 같은 bulk put 안에 서로 다른 값을 넣으려고 할 때 어떤 행동이 예상되는지 결정해야 합니다. 아마 그냥 터트려버리는 게(blow up) 나을 것 같네요. 그건 잠시 후에 생각하죠. 모든 가능한 단계... 메타 데이터 다시 쓰기 체크... C. 오, 'Radix sort(기수 정렬)'가 있네요. 왜 안 되겠어요? 타이핑 대신 클릭을 했어야 했는데. 좋아요. 이런 맛에 사는 거죠. IDE는 이런 종류의 작업에는 정말, 절대적으로 잘못된 도구예요. 업데이트. 그래, 가라.

좋아요. 좋아요, 이제 봅시다. 지니를 사용할 때 좋은 점 중 하나는 말할 시간이 생긴다는 거예요. 2분, 3분, 5분 정도의 시간이 생기죠. 저는 페어 프로그래밍 + 지니 조합을 좋아해요. 그냥 롤백하고 커피 마시러 갈 시간이 충분하거든요. "우리 뭐 할까?" 하고 이야기할 수 있죠. 실제 진전이 이루어지고 있고요. "커피가 더 필요해." 커피 더 필요하다는 말이 나와서 말인데, 다들 카페인 충분히 섭취하고 계시죠? 환영합니다 여러분. 시청해 주셔서 정말 감사해요. 제가 여러분을 볼 수 없고 누가 있는지 몰라도 괜찮습니다. 그냥 혼잣말할게요. 어차피 저는 하루 종일 거의 혼잣말하거든요. 그러니까요. 저는 주의력 결핍이 있는 건 아니에요. 확실히 해두죠. 저는 단지 '지루함'에 극도로 민감할 뿐입니다. 뭔가 지루해지자마자 제 뇌는 딴 데로 가버려요. 저는 이걸 '지루함 민감도(boredom sensitivity)'라고 부릅니다. "다음 단계 계획 중. GitHub 리포지토리 설정." 그게 뭔 소린지는 모르겠는데, 여기 있네요. 맘(Ma'am), 이건 뭐죠? 흠. 네, 무작위 생각을 뱉어낼 게 떨어지면 곧 중단시킬 겁니다.

정말 유혹적이죠. 여기 연산들이 있고, 데이터 구조가 있고, 연산 몇 개가 있는데, 하나를 끝내면 바로 다음 걸로 넘어가고 싶거든요. 그 사이에 '옵셔널리티(optionality)'를 개선할 틈이 있다는 걸 알아채는 게 중요해요. 이 경우에는 이 연산들을 별도의 스코프(scope)로 빼내는 거죠. 이걸 '스코프'라고 부릅시다. 이 op 구조체들은 서로 비슷할 테니, 나중에 더 쪼개낼(pull out) 기회를 찾을 겁니다. 하지만 먼저 GPU sorted map에서 모든 걸 끄집어내서 이 연산 구조체들로 옮겨야 해요. 그러고 나면 우리를 도와줄 추가적인 추상화를 찾을 수 있겠죠. 제가 말했듯이 29명의 라이브 시청자가 있네요. "에이전트가 어떻게 실행되는지, 코드를 보호하기 위해 어떤 가드레일(guardrails)을 뒀는지 1분 정도 설명해 주시면 첫 세션을 놓친 사람들을 따라잡는 데 도움이 될 것 같아요." 좋아요. 이건 라이브 코딩, 라이브 스트리밍 코딩인데 제가 아직 라디오 용어로 '리셋(The Reset)'이라고 부르는 걸 안 했네요. "돌아왔습니다. 우리는 지금 블라블라에 대해 이야기 중입니다." 같은 거요.

현재 작업, 29명 라이브 시청자. 제 기록인 것 같네요. 이건 유료 구독자뿐입니다. 유료 구독자만 이걸 라이브로 볼 수 있어요. 나중에 유튜브에 올라가면 죽은 비디오가 될 거고 사람들은 거기다 댓글 달고 온갖 못된 말을 할 수 있겠죠. 하지만 여기 계신 여러분, 정말 감사합니다. '리셋'. 우리가 풀고 있는 문제는 '지니 세션'이라고 부릅니다. 지니와 함께 코딩하는 거죠. 지금 지니가 무한 루프에 빠진 것 같지만, 중단시킬 겁니다. 지니 세션, 우리는 지니와 함께 코딩합니다. 우리가 해결하려는 문제는 '정렬된 맵(sorted map)'인데, 항목들을 GPU에 저장하고 업데이트도 GPU에서 하는 겁니다. 네 가지 연산이 있어요. get, put, delete, 그리고 키와 키 사이 iterate. 지난주에 기본 기능을 구현하는 데 1시간을 썼고, 지금 구조 잡는 데 30분을 썼는데 지니가 멈춘 것 같네요. 좋아요. "너 멈췄니?" 그냥 종료할게요. "다음 단계를 계획한다고 주장하지만 정말 복잡하고 컸니? 기존 셰이더를 탐색해서 재사용 가능한 GPU 정렬 부분을 찾거나, GPU 정렬이 명시적으로 금지되었으니 단순화된 GPU 계획을 세워야 하니?" 네가 이걸 쉽게 하려고 하는 거니? 난 네가 편한 거엔 관심 없어. 네가 나를 편하게 해주는 데 관심이 있지. 숀(Sean), 이게 원하셨던 '리셋'이었으면 좋겠네요. 와, 생각 정말 열심히 하네요. "네, 더 많은 업데이트를 원합니다." 완벽해요. 감사합니다. 천만에요. 이거 재밌네요.

제 오랜, 아주 오랜 친구 론 제프리스(Ron Jeffries)가 훌륭한 비평을 해줬어요. 우린 친구로 지낸 지 오래됐죠. 그가 정말 늙기도 했고요. 뭐 그건 별개의 문제지만요. 지니와 코딩하는 것에 대해, 그리고 그것이 기후에 미치는 영향이나 권력의 집중 효과에 대해 걱정하는 내용이었죠. 이해합니다. 제 생각에 앞으로 나아가는 가장 효과적인 길은 이것과 관계를 맺는(engage) 겁니다. 제 인생 단계, 제 경력 단계에서는요. "아까 연산을 별도 모듈로 옮기는 걸 고려하셨는데, 단점이 있나요?" 아, 만약... 좋아요, 현재 상태. 이럴 때 그림을 끄적거릴 수 있으면 좋겠는데. 창이 있어야 하는데 나중에 해결하죠. 연습 세션을 가져서 누가 좀 더 잘하는 법을 알려주시면 좋겠네요. 어디까지 했죠? 연산을 별도 모듈로 옮기는 것. 좋아요. 만약 파일 하나에 모든 게 있고 이리저리 옮겨야 한다면, 그건 비교적 쉽습니다. 연산을 별도 파일로 옮기고 나서 그 사이에서 뭔가를 옮겨야 한다면, 그건 더 어렵습니다. 그래서 제가 이 put 연산처럼 뭔가를 연산 내부로 옮기려 할 때, 저는 여전히 파일 하나에 두는 걸 원합니다. 요소들이 불편할 정도로 커져도 상관없어요. 저는 생물학 비유를 듭니다. 세포가 점점 커지다가 좀 늘어나는 것 같으면 그때 분리됩니다. 그게 분리되는 시점이죠. 앉아서 세포 공장을 짓고 아주 작은 걸 만든 다음 각각을 키우는 게 아닙니다. 아니에요. 하나를 가지고 있다가 커지면 쪼개는 겁니다. 그게 제가 하고 싶은 방식이에요. 그리고 현실적으로, 피할 수 있다면 파일 간 이동을 많이 하고 싶지 않아요. 해야 한다면 뭐, 하겠지만요.

"단계적인 put, bulk put 리팩터링 계획. GPU 정렬, 스캐폴딩(scaffolding), 스레드 공간, 새로운 구조체 파라미터 도입, 그리고 입력, 정렬, 스캔, 중복 제거, 분산(scattering)을 위한 버퍼 재구성. 초기에는 CPU 폴백(fallback) 지원." 아니, 하지 마. CPU 폴백 없음. "만약 WGPU를 사용할 수 없으면 중단(Abort)해." 좋아요, 큐에 들어갔습니다. 와, 이게 통과하면 정말 놀랍고 기쁠 겁니다. 하지만 그게 다예요. 피트(Pete) 님이 묻네요. "안녕하세요 피트. 그래도 지니한테는 더 어려운 거 아닌가요?" 네, 지니한테는 더 어렵죠. 컨텍스트 윈도우(context window)를 확장시키고, 실수할 확률을 높이고, 멈출 확률도 높입니다. 자, 여기 있습니다. "bulk put을 위한 거대하고 새로운 WGSL 셰이더 문자열 초안 작성." LibRS를 보면 GPU 소스 코드가 아래쪽에 있어서 스크롤도 안 해봤죠.

필립(Philip), "와, 전설이시네요. 세션이 있는 줄 몰랐는데 클릭해 버렸어요. 쇼 예약하러 가야 해서요. 쇼 즐기세요, 필립. 주말 잘 보내시고요. 안전하다면 과용(overindulge)하셔도 됩니다." 그게 제 조언입니다. "론 제프리스의 글은 어디서 볼 수 있나요? 블로그에서 활동하시나요?" 블로그는 잘 모르겠고, 론의 질문은 예전 채팅 스레드에 있었던 것 같아요. 누군가 도와주시겠죠. 제가 지루함 민감도가 있어서 이런 곳이 참 좋아요. 우리 다 그렇잖아요. 하나의 응집력 있고 일관된 스레드를 처음부터 끝까지 원하신다면, 하! 론의 댓글은 아마 이전 채팅 스레드에 있었을 겁니다. 누군가 찾는 걸 도와주실 수 있을 거예요. 저는 여전히 기다리고, 희망하고, 기도하고 있습니다. 지니가 작동하는 코드를 주기를, 그리고 그게 다 작은 스코프 안에 있기를요.

좋아요. 연산 간의 중복을 발견했을 때 상상할 수 있는 경로가 많습니다. 제가 하고 있는 건 각 연산을 위한 스코프를 만드는 것이고, 그 사이에 상당한 중복이 있을 거라고 예상합니다. 무언가를 설정하고, 메모리 버퍼를 설정하고, 연산을 설정하고 사용하는 과정에서요. 아, 저는 커서(Cursor)를 쓰고 있습니다. 커서는 이 쇼의 유료 스폰서가 아닙니다, 아직은요. 이 지니 세션을 후원할 만한 사람을 아시면 저한테 보내주세요. 이게 제 생계수단이기도 하거든요. 아, 이게 론 제프리스에 대한 제 답변의 일부입니다. 그는 은퇴했고 수입이 필요 없으니 정말 훌륭하고 환상적이죠. 그는 일을 했고 보상을 받고 있어요. 제 동기의 일부는 여전히 생계를 유지하고 집값을 내야 한다는 겁니다. 그래서 그게 분명히 일부고요. 인류의 공익을 위해서만 하는 척하고 싶진 않아요. 저는 제 동료 괴짜(geek)들이 세상에서 안전하다고 느끼도록 돕기 위해 이 일을 합니다.

오, 필립, 저를 탈선시킬 순 없어요. 왜냐면 레일(rail)이 없거든요. 하지만 저는 이 일을 정말 사랑하기 때문에 하기도 합니다. 그냥 이 안에 있는 게 좋아요. 코드가 잔뜩 있고 정말 복잡해서 이해는 못 하겠지만 진전을 만들어야 하잖아요. 아, 정말 천국이에요. 근육을 쓰고 있고, 솔직히 말해서 프로그래밍할 때 겪는 온갖 귀찮은 일들에 질려버렸거든요. 이제 운동할 수 있게 됐죠. 고마워요, 피트. 그건 티셔츠네요. "루트 2008 루비 온 레일즈, 2026 켄트 오프 레일즈(Kent off rails)." 어디까지 했죠?

좋아요, 왜 이걸 하고 있냐고요? 첫째, 먹고살아야 하니까. 둘째, 정말 즐기니까. 셋째, 모두가 증강 개발(augmented development)에 대해 패닉 상태니까요. 사람들은 그 패닉에 다양한 방식으로 반응합니다. 생각 없는 치어리더들도 있고요, 저도 치어리더였으니 그게 어떻게 돌아가는지 알죠. 생각 없는 비평가들도 있습니다. "난 절대 저런 거 안 건드릴 거야." 하면서 꽤 감정적이죠. "이건 도덕적 입장이야. 거대 언어 모델(LLM)을 건드리면 넌 사악하고 끔찍하고 블라블라." 저는 그게 도움이 된다고 생각하지 않아요. 그 중간에 있는 많은 사람들, 발을 담그는 사람들, 아직 준비가 안 된 사람들...

필립이 AI에 대한 지혜를 구하네요. 그건 지금 시점에서는 너무 어려운 주문입니다. 하지만 제가 판단할 일은 아니죠. 저는 그냥 제 일을 하고 있고, 지난 몇 년간 해왔던 대로 공개적으로 하고 있을 뿐입니다. "입력 연산(바인드 그룹 레이아웃 및 버퍼 포함) 가져오기, 정렬 및 중복 제거, 그리고 기수 정렬(radix sorts)이 정확하다고 확인된 후 버퍼 사용. 병합을 위한 컴팩트 버퍼가 보존되도록 보장." 좋아요, 굿, 굿, 굿. 이걸 했다고 주장하네요. 지금 무슨 생각 중일까요? 할 일 목록(To-do)이 보이는데 여기 뱅글뱅글 도는 게 있네요. 돌아가는 게 보여요. 여러분한테 보이는지 모르겠네요. 그리고 GitHub 리포지토리 설정 같은 게 있는데 그게 뭔지 모르겠어요. 아직도 하고 있네요.

좋아요, 나의 사람들. 우리 괴짜들은 패닉 상태고, 제 임무는 괴짜들이 세상에서 안전하다고 느끼게 돕는 겁니다. 이건 언제 한번 깊게 얘기해보죠. 제 서브스택 'About'을 보시면 나옵니다. 저는 이걸 사랑하고 제 커뮤니티에 대한 봉사이기 때문에 제 청구서를 지불합니다. 제 생각에 이건 믿을 수 없을 정도로 강력한 도구입니다. 우리가 쓰든 안 쓰든 우리 삶에 영향을 미칠 거고, 우리 직업적 삶에 영향을 미칠 겁니다. 잃을 것도 많지만, 잠재적으로 얻을 것도 많습니다. "버퍼 바인딩과 스캔, 디스패치 평가 중." 보통 이렇게 오래 걸리진 않는데. 그냥 롤백하고 싶은 유혹이 드네요. 15분 남았는데 진전을 좀 보고 싶거든요. 덩어리가 너무 컸나 봅니다. 아까 더 단순한 정렬이나 기수 정렬을 쓸지 물어봤을 때 결정을 내렸어야 했는데. 한 2~3분 더 줘보고, 안 되면 그냥 롤백할게요. "테스트는 실행되지 않겠지만 트리거될 수 있음." 실행해. 아, 여전히 생각 중이라고 하네요.

좋아요. 지금 이 지니는 '하위 호환성(backwards compatibility)'에 집착하고 있습니다. 아무도 호출하지 않는 코드를 위해 하위 호환성을 유지하려고 엄청난 복잡성을 추가하려 하고 있어요. 이건 그냥 GitHub에 있는 수많은 리포지토리를 봤기 때문에 나타나는 증상 같아요. 거기엔 하위 호환성 코드가 잔뜩 있으니까요. 하지만 왜 하위 호환성이 유용한지, 언제 유용한지, 언제 필요 없는지에 대한 개념이 없어요. 그냥 멍청한 봇을 만들기 위해 엄청난 복잡성을 날려버릴 기세네요. 좋아요, 뭐 어때요. "나한테 바징가라고 말하지 마, 네가 바징가야." 됐습니다. 이렇게 작은 튜닝을 하는 게 참 좋아요. 이제 cargo test를 실행할 수 없나요? 누가 "이번만 실행해"라고 좀 말해주세요. 매번 실행하는 건 어때요? 뭐 괜찮습니다. 오오, 문제가 생겼지만 고칠 겁니다. 그게 좋은 점이죠. 이제 실행해. 아니. 테스트 실행 중. 여기 있습니다. 필립. 다음 시간까지, 당신 최고예요. 이제 cargo test 실행. 성공. 그리고 얼마나 빠른지 기억할 겁니다. 으, 뭔가 실패했네요. 시청해 주셔서 정말 감사합니다. 우리는 지니를 사용해서 기본 데이터 구조인 정렬된 맵을 구현하고 있습니다. 세 가지 연산이 있죠. 네 번째 연산도 추가해야 합니다. 키와 키 사이 순회(iteration)를 추가해야 해요. 아니, 뭐가 잘못됐지? Get def. Get log. Get push. 좋아요. 리포지토리는 Kent backslash GPU sorted map이고 우리는 optionality 브랜치에 있습니다. "테스트와 성능 측정, 커밋 및 푸시 완료." 좋아요. PR을 열고 싶으면 말만 하세요. 아니, PR 안 열어. 아니요.

자, 어디까지 왔는지 봅시다. 아, 아까 ops, 이 연산들을 언제 별도 파일에 넣느냐는 질문이 있었죠. 가능한 모든 것이 이동된 후입니다. 좋아요. 그냥 물어볼게요. "가능한 모든 것이 op 구조체로 옮겨졌니? 그래서 우리가 ops.rs 헬퍼 파일을 추출할 수 있니?" 물음표 없이. 말하는 동시에 타이핑했는데, 이게 사실일까요? 곧 알게 되겠죠. 10분 남았습니다. 와우. 시청해 주셔서 정말, 정말 감사합니다. 아무도 안 봐도 저는 이걸 하지만, 사람들이 보고 있으면 더 열심히 하죠. 안 보신 분들에 대해서는 우리가 원하는 대로 험담을 해도 됩니다. 어차피 나중에야 볼 테니까요.

마이크(Mike)님이 묻습니다. "AI가 인간과 코드를 읽는 능력이 다르다는 주장이 있을 수 있겠네요. 그래서 우리가 같은 방식으로 정리(tidy)할 필요가 없을 수도 있고요. 우린 여전히 인간이 읽으라고 코드를 짜고 있나요? 아니면 AI가 읽으라고 짜는 걸 생각하고 있나요?" 사고 실험(Thought experiment). 만약 LLM이 다루기엔 환상적이지만 인간이 읽기는 불가능한 프로그래밍 언어를 만들었다면 어떨까요? 인간에게 편한 언어 대신 그 언어를 사용할까요? 제 생각에 대답은 '예스'입니다. 시스템이 우리가 원하는 대로 행동할 거라고 신뢰하는 순간 그렇게 되겠죠. 피트, 천만에요. 이건 그냥 괴짜가 괴짜 짓 하는 겁니다. 카메라가 안 돌아가도 했을 거예요.

어디죠? 어디에 있죠? 좋아요. "성능 트렌드를 확인해야 해. 대용량 데이터셋에 대해서." 오, 뭘 한 거죠? LibRS에서 GPU 코드를 빼내는 것 같네요. 좋아요. 좋아요. 왜냐하면 LibRS는 진입점(entry point)이니까요. 내비게이션의 시작이죠. 거기엔 사실 GPU 관련 내용이 많이 필요 없어요. 이 bulk get op는 GPU 코드를 호출할 거고, 그 GPU 코드가 호출하는 연산 가까이에 있었으면 좋겠어요. 왜냐하면 하나를 바꾸면 다른 하나도 바꿔야 하니까요. 고전적인 'Tidy First', 응집도(cohesion)와 결합도(coupling)죠. 네. 굿, 굿, 굿, 굿. 작동한다면 좋겠죠. 이게 제가 보고 싶었던 거예요. 전 이 모든 걸 알지 못해요. 하지만 필요한 부분은 알아낼 수 있죠. 그게 핵심입니다. 왜냐하면 우리는 괴짜니까요. 왜 실행 중이죠? "절반쯤 왔어. 바징가." 지니 세션 시간이 몇 분 안 남았네요.

숀이 말합니다. "멋진 팁 하나. 며칠 전에 다른 분한테 제안했던 건데요. 지니가 새 파일을 생성하는 대신, 파일과 함수 맵 그리고 생성된 설명을 포함하는 파일을 만드는 거예요. 그리고 시스템 프롬프트에 에이전트가 그 파일을 먼저 참고하도록 설정하면 토큰 사용량과 컨텍스트 부하를 줄일 수 있어요." 네, 네, 네. 다음엔 그걸 시도해 보죠. 목차(table of contents) 같은 거네요. 그리고 성능을 좀 저장할 겁니다. "대용량 데이터셋에서 성능 추세가 어떤지 물어봐야 해." 오, 뭘 했죠? LibRS 파일에서 GPU 코드를 빼낸 것 같네요. 좋아요. LibRS는 기본 파일이니까요. bulk get op가 GPU 코드를 호출할 텐데, 우리는 그 GPU 코드가 호출하는 연산과 가까이 있기를 원합니다. 왜냐하면 하나를 변경하면 다른 하나도 변경해야 하니까요. 전형적인 Tidy First, 응집도와 결합도죠. 네. 굿, 굿, 굿. 작동한다면 굿이죠. 이게 제가 보고 싶었던 겁니다. 저는 이 내용을 다 알지 못하지만, 필요한 부분은 무엇이든 알아낼 수 있습니다. 그게 핵심이죠. 왜냐하면 우리는 괴짜(geeks)니까요. 왜 실행 중이지? 바징가. 절반쯤 왔네요. 바징가. 지니 세션이 몇 분 안 남았습니다.

마이크, 우리가 인간을 위해 코드를 짜고 있냐고요? 네, 여전히 인간을 위해 짜고 있습니다. 왜냐하면 인간이, 이걸 학습 기회로 삼는다면 코드가 어떻게 생겼느냐가 제가 얼마나 쉽게 읽을 수 있느냐를 바꾸거든요. "아, WGPU는 정말 복잡하고 지저분한 저수준 API를 가지고 있어. 작동만 하면 누가 신경 써?"라고 할 수도 있겠죠. 하지만 저는 신경 씁니다. GPU에서 뭐가 가능한지 배우고 싶거든요. 이해하지 못하면 배울 수 없어요. 그래서 저에게는 당분간 코드가 어떻게 읽히느냐가 정말 중요합니다. 응집도가 높고 결합도가 낮은 요소들을 가지는 게 제가 이해하는 데 차이를 만듭니다. 그리고 제가 이해하지 못하면, 지니가 실수하는 걸 막을 수 없어요. 지니는 실수하기 쉬우니까요.

와우. "CPU put 대 GPU put, 백만 개 요소에서 GPU가 3배 더 빠름. get은 30배 더 빠름." 와우. 와우. 와우. 좋아요. 흥미롭네요. 저는 계속 작업할 겁니다. 여러분도 한번 시도해 보셨으면 좋겠어요. 이건 코드가 꽤 복잡해서 지니가 그냥 알려진 솔루션을 복사할 수 없거든요. JUnit 작성해달라고 하면 그냥 JUnit 써주겠죠. JUnit은 백만 번도 더 봤으니까요. 이건 복잡한 물건입니다. 최고 시청자 수 36명 찍었네요. 유료 구독자만요. 정말 감사합니다. 유료 구독해주셔서 정말 감사해요.

이건 조만간 유튜브에 올라갈 겁니다. GitHub 리포지토리 알려드렸죠? Kent backslash GPU sorted map. 포크(fork) 하시고, 지니 돌려보시고, 백만 가지 시도해 보시고, 결과물 잔뜩 버릴 준비 하시고, 배우세요. 학습을 극대화하세요. 제가 딱 하나만 말할 수 있다면, 바로 그겁니다. 질문과 댓글 주신 모든 분들 정말 정말 감사합니다. 실시간 사용자 인터페이스가 뭐든 간에 찾아서 더 소통할 방법을 알아낼게요. 찾는 즉시요. 여러분 모두 환상적인 주말 보내시길 바랍니다. 저는 포커 치러 갈 겁니다. 지금 당장은 아니고요. 일 좀 더 하고요. 포커 치러 갔다가 '게 요리 파티(crab feed)'에 갈 겁니다. 그게 제 주말이에요. 즐거운 시간 되세요. 평화를 빕니다(Peace).