플러그인 성능 향상하기

플러그인을 만들었지만...

최근 mlua.nvim이라는 플러그인을 만들었습니다.

열심히 LLM의 도움을 받아서 mlua 언어 서버를 Neovim에 붙이는 플러그인을 만들었고, 다행히도 어느 정도 쓸만한 수준으로 완성할 수 있었습니다.
하지만, 동작을 하게 하는 데는 성공했지만, 성능이 너무 안 좋아서 실제로 쓰기에는 무리가 있었습니다.

이 이야기는 어떻게든 이 플러그인을 써먹기 위해 고군분투한 이야기입니다.

문제점 파악하기

너무 느리다

일단 플러그인이 동작은 하지만, 너무 느렸습니다.

파일을 열 때마다 엄청난 렉이 걸렸고, 편집 도중에도 자주 멈추는 현상이 발생했습니다.
문제의 원인을 파악하기 위해, 먼저 플러그인의 구조를 다시 살펴보았습니다.

플러그인은 기본적으로 Neovim의 LSP 클라이언트를 사용하여 mlua 언어 서버와 통신하는 구조였습니다.
문제는, 파일을 열 때마다 언어 서버와의 초기화 과정에서 많은 시간이 소요된다는 점이었습니다.
특히, 플러그인이 파일을 열 때마다 언어 서버를 새로 시작하는 방식으로 구현되어 있었기 때문에, 매번 초기화 과정에서 렉이 발생했습니다.

비효율적인 파일 탐색

또한, 플러그인은 파일을 열 때마다 프로젝트 디렉토리를 탐색하여 관련 파일들을 로드하는 방식을 사용하고 있었습니다.
이 과정에서 많은 파일을 읽어들이면서 시간이 많이 소요되었고, 특히 대규모 프로젝트에서는 더욱 심각한 성능 저하가 발생했습니다.

성능 향상 방안

언어 서버 재사용

첫 번째로, 언어 서버를 매번 새로 시작하는 대신, 한 번 시작된 언어 서버를 재사용하는 방식을 도입했습니다.
이를 위해, 플러그인 내부에 언어 서버 인스턴스를 캐싱하는 로직을 추가했습니다.

비동기 파일 로딩

두 번째로, 파일 탐색 및 로딩 과정을 비동기 방식으로 변경했습니다.
이를 위해, Neovim의 vim.loop 모듈을 활용하여 파일 읽기 작업을 비동기로 처리하도록 수정했습니다.
이렇게 하면, 파일을 읽는 동안에도 Neovim이 멈추지 않게 되었습니다.

캐싱 메커니즘 도입

세 번째로, 자주 접근하는 파일들의 내용을 캐싱하는 메커니즘을 도입했습니다.
이를 통해, 동일한 파일에 대한 반복적인 읽기 작업을 줄일 수 있었습니다.

... 라는 계획을 세우고 구현했지만, 여전히 성능이 만족스럽지 않았습니다.
애초에 진짜 원인은 다른 데 있는 게 아닐까 하는 생각이 들었습니다.

근본 원인 찾기

원본 익스텐션 분석

문제가 해결되지 않자, 저는 원본 mlua 익스텐션의 동작 방식을 다시 한 번 분석하기로 했습니다.

원본 익스텐션은 TypeScript로 작성되어 있었고, tmlanguage 파일을 사용하여 문법 강조를 처리하고 있었습니다.
여기까지는 분석한 대로였지만, 파일 탐색과 관련된 부분에서 중요한 단서를 발견했습니다.

원본 익스텐션은 파일을 열 때마다 전체 프로젝트 디렉토리를 탐색하는 대신, 변경된 파일만을 추적하여 필요한 파일들만 로드하는 방식을 사용하고 있었습니다.
이벤트 기반의 파일 시스템 감시 기능을 활용하여, 파일 변경 시에만 로드를 수행한다는 아이디어를 얻은 저는 이를 Neovim 플러그인에 적용하기로 했습니다.

이벤트 기반 파일 감시

다행히, 단순히 파일 경로에 대한 인덱싱 작업은 비동기로 처리할 수 있었고, 현재 편집 중인 줄에서만 키워드를 추출해 인덱싱된 파일 경로에서 검색하는 방식으로 변경할 수 있었습니다.
여기서 퍼지 매칭 알고리즘을 도입하여, 키워드와 유사한 파일 경로를 빠르게 찾을 수 있도록 했습니다.

이를 통해, 파일 탐색 과정에서의 불필요한 작업을 크게 줄일 수 있었지만... 더 근본적인 문제는 전혀 다른 데 있었습니다.

WSL2를 쓴 게 문제였다

결국, 문제의 근본 원인은 WSL2 환경에서의 파일 시스템 성능 저하였습니다.

WSL2는 리눅스 커널을 가상화하여 윈도우에서 리눅스 환경을 제공하는 방식이기 때문에, WSL2 내에서 윈도우 파일 시스템에 접근할 때 상당한 성능 저하가 발생합니다. 특히나 대규모 프로젝트에서는 파일 탐색 및 로딩 과정에서 큰 병목 현상이 발생했습니다.

애초에 Windows 환경에서만 지원하는 메이플스토리 월드의 특성상, Windows 네이티브로 VSCode를 사용할 것으로 예상하고 만든 익스텐션과 달리 WSL2 환경에서 Neovim을 사용하면서 발생하는 성능 문제는 고려 대상조차 아니었던 것입니다.

지금까지의 삽질은 뭐였지

이 사실을 깨닫고 나니, 지금까지의 삽질이 허무하게 느껴졌습니다.

물론, 제가 한 모든 최적화들이 쓸모없었던 것은 아닙니다. 하지만, 진짜 문제는 WSL2 환경에서의 파일 시스템 접근 속도였던 것입니다.
Windows 버전 Neovim을 사용하여 직접 윈도우 파일 시스템에 접근하는 방식으로 플러그인을 실행했을 때, 성능이 크게 향상되는 것을 확인할 수 있었습니다.

... 어쩌면 지금까지의 삽질을 굳이 안 하고도 초기 버전만으로도 충분했을지도 모르겠다는 생각이 들었습니다.
원본 익스텐션이 VSCode에서 잘 돌아가던 이유도 여기에 있었겠지요.

(실제 플러그인 동작 모습)

mluademo

결론 및 앞으로의 계획

이번 경험을 통해, 무언가 개발하는 과정에서 환경의 특성을 충분히 고려하는 것이 얼마나 중요한지 다시 한 번 깨달았습니다.

어쨌든, 플러그인은 이제 WSL2 환경이 아닌 Windows 네이티브 환경에서 잘 동작하고 있습니다.
Windows인게 그렇게 썩 마음에 들진 않지만, 어쩔 수 없지요. 애초에 메이플스토리 월드가 Windows 전용이니...

앞으로도 플러그인을 지속적으로 개선해 나가면서, 다양한 환경에서의 성능 최적화 방안을 모색할 계획입니다.
특히, WSL2 환경에서의 성능 문제를 해결할 수 있는 방법을 찾아보는 것도 중요한 과제가 될 것입니다.
이래도 메이플스토리 월드가 Linux 지원 안 하면 Windows에서만 쓰는건 똑같겠지만

어쨌든 뭐 언젠가는 쓸모가 있겠지요.

삽질도 경험이라면 경험이니까요.