AVIF 변환은 왜 이렇게 오래 걸릴까요 — 12MP 실측 이야기

기본값을 바꾸게 만든 숫자

이미지 압축기를 만들 때 AVIF는 하마터면 못 넣을 뻔한 포맷이었어요. 1200만 화소짜리 사진, 그러니까 휴대폰이나 카메라로 찍은 평범한 사진 한 장을 인코더 speed 7 설정으로 AVIF로 바꾸는 데 143초가 걸렸거든요. 이미지 한 장에 2분 반이에요. 그것도 브라우저 탭 안에서, 로딩 표시만 돌아가고 멈춘 건지 아닌지도 알 수 없는 상태로요.

speed 9로 바꾸니 같은 사진이 40초 정도로 줄었어요. 이것도 빠르다고 할 순 없지만, 쓰는 도구와 닫아버리는 도구를 가르는 차이예요. 지금 압축기가 쓰는 값이 speed 9이고, 나중에 누가 좋은 뜻으로 슬쩍 바꿔놓지 않도록 호출 바로 위에 이유를 적어뒀어요.

직접 재본 건 이 두 개뿐이라, 인용할 수치도 이 두 개뿐이에요. 왜 이런 숫자가 나오는지가 이 글의 내용이고요.

인코더가 그 시간에 하는 일

AVIF는 정지 이미지 포맷으로 따로 자란 규격이 아니에요. AV1 영상의 한 프레임을 컨테이너에 담은 형태예요. AV1은 영상 스트림을 계산이 허락하는 한 최대로 압축하려고 설계됐고, 그 전제는 “인코딩은 서버에서 한 번, 재생은 휴대폰에서 수백만 번”이었어요. 이 비대칭이 규격에 그대로 박혀 있어요. 푸는 건 빠르고 싸게, 만드는 건 대놓고 비싸게요.

비싼 이유는 탐색이에요. 이미지의 블록마다 인코더는 예측 방식, 블록을 쪼개는 방법, 변환 옵션을 아주 많은 조합에서 고를 수 있고, 조합마다 용량과 화질 결과가 달라져요. 전부 따져보면 제일 좋은 답이 나와요. 그리고 영원히 안 끝나죠. 그래서 인코더는 이 탐색 공간을 얼마나 돌아볼지 정하는 손잡이를 열어둬요. 저희가 쓰는 라이브러리에서는 speed, 다른 데서는 effort나 cpu-used라고 부르는 값이에요.

숫자가 낮으면 넓게 탐색해요. 느리고, 파일은 작아져요. 숫자가 높으면 인코더가 지름길을 타요. 제일 좋은 선택을 찾는 대신 괜찮은 선택이 나오면 받아들이죠. 빠르고, 같은 화질 기준에서 파일이 조금 커져요. 화질 슬라이더가 아니라는 점이 중요해요. 화질 값은 디테일을 얼마나 버릴지 정하고, speed 값은 그 화질에 도달하는 데 얼마나 공을 들일지 정해요.

거래 조건은 그게 전부예요. 느린 설정은 용량을 벌어줘요. 얼마나 벌어주는지는 이미지마다 다르고, 저희는 설정별 시간만 쟀지 용량까지 재지는 않았으니 없는 퍼센트를 지어내지는 않을게요.

데스크톱 프로그램보다 브라우저가 느린 이유

같은 파일을 데스크톱 프로그램으로 돌리면 대개 위 수치보다 빨리 끝나요. 인코더 자체와는 무관한, 구조적인 이유가 두 가지 있어요.

스레드예요. 데스크톱 인코더는 가진 코어를 전부 나눠 써요. 브라우저에서 멀티스레드 WebAssembly를 쓰려면 SharedArrayBuffer가 필요하고, 그러려면 페이지에 COOP·COEP 헤더를 걸어 교차 출처 격리를 켜야 해요. 이 사이트는 그 헤더를 일부러 넣지 않아요. 교차 출처 격리를 켜면 도구를 무료로 유지해 주는 광고 iframe이 깨지거든요. 그래서 여기 코덱은 전부 단일 스레드로 도는 게 의도된 설계예요. 143초와 40초는 단일 스레드 기준 수치예요. 네 코어짜리 데스크톱 인코더가 일을 덜 하는 게 아니라, 같은 일을 나눠서 동시에 하는 거예요.

병렬 처리는 다른 방식으로 되찾았어요. 압축기는 독립적인 워커를 여러 개 띄우는데, 개수는 코어의 절반이고 최대 3개, 모바일이나 메모리가 적은 기기에서는 1개예요. 여러 장을 한꺼번에 돌릴 때는 확실히 빨라져요. 대신 큰 사진 한 장에는 아무 도움이 안 돼요. 한 장의 인코딩은 워커 하나 안에서 끝나니까요.

메모리 부담도 있어요. AVIF는 이 중에서 제일 무거운 코덱이라, 저희 압축기는 워커가 남아 있어도 AVIF 인코딩을 절대 두 개 동시에 돌리지 않아요. 노트북에서 동시에 여러 개를 돌리면 메모리가 눌리고 기기가 뜨거워지는데, 실제로 줄어드는 시간이 그 대가만큼 되지 않아요. AVIF로 여러 장을 걸어두면 일부러 하나씩 처리해요.

저희가 잰 값

설정 이미지 결과
speed 7 12MP 143초
speed 9 12MP 약 40초

이 표는 딱 이만큼으로만 읽어주세요. 개발 중에 한 대의 기기에서, 브라우저 단일 스레드 WebAssembly로 잰 두 개의 값이고, 인코더를 벤치마크하려던 게 아니라 “이거 내보내도 되나?“라는 제품 판단을 하려고 잰 수치예요. 기기와 브라우저, 이미지에 따라 시간은 달라져요. 일반화할 수 있는 건 초 단위 숫자가 아니라 곡선의 모양이에요. speed 눈금 몇 칸 차이가 인코딩 시간을 몇 배로 불릴 수 있다는 것, 그래서 “AVIF는 느리다”는 말이 포맷의 성질이라기보다 누군가 대신 골라준 설정의 성질에 가깝다는 것이요.

같은 이유로 겪었을 법한 일도 설명이 돼요. 두 도구가 똑같이 AVIF를 지원한다고 해놓고, 하나는 몇 초 만에 끝나고 하나는 멈춘 것처럼 보이는 경우요. 같은 인코더를 다른 설정으로 돌리고 있을 가능성이 커요.

기다릴 만하게 만드는 방법

  • 인코딩 전에 크기를 줄이세요. 인코딩 비용은 화소 수를 따라가는데, 대부분의 이미지는 실제로 보여줄 크기보다 훨씬 크게 내보내져요. 가로세로를 절반으로 줄이면 훑을 화소가 4분의 1이 돼요. 저희 압축기는 리사이즈를 같은 과정에서 처리하고, 손댈 수 있는 것 중에 효과가 제일 큰 방법이에요.
  • 큰 사진은 한 장씩, 작은 이미지는 묶어서요. 워커 여러 개는 웹용 크기 이미지 한 폴더에는 큰 도움이 되지만, 2400만 화소 사진 한 장에는 전혀 도움이 안 돼요.
  • 기다릴 값어치가 없다 싶으면 WebP로 가세요. WebP는 훨씬 빨리 인코딩되고, 어디서나 열리고, 대부분의 작업에는 충분히 작아요. AVIF로 더 줄이는 값이 시간을 들일 만한지는 포맷 비교 가이드에서 다뤘고, 흔한 경우는 PNG를 WebP로 바로 처리할 수 있어요.
  • 느린 인코딩과 느린 포맷을 헷갈리지 마세요. AVIF를 화면에 띄우는 건 빨라요. AV1을 만든 사람들이 최적화한 게 그쪽이거든요. 비용은 내보낼 때 한 번, 만드는 사람이 치러요. 사이트를 보러 온 사람은 그 비용을 안 내요.
  • 화질 숫자는 다르게 잡으세요. 저희 압축기의 기본 화질은 WebP·JPEG가 75인 반면 AVIF는 50이에요. 인코더마다 눈금의 의미가 달라서예요. 습관대로 AVIF를 75로 올리면 더 좋아 보이는 대신 생각보다 훨씬 큰 파일이 나와요.

반대 방향은 금방이에요

AVIF가 정말 힘 안 들이는 쪽은 푸는 쪽이라, AVIF로 만드는 건 느려도 AVIF에서 빼내는 건 빨라요. 받은 AVIF를 클라이언트 쪽 구형 프로그램이 못 연다면, AVIF를 PNG로나 AVIF를 JPG로 바꾸는 건 AVIF 내보내기에 드는 시간의 아주 일부면 끝나요. 비싼 탐색은 그 파일을 만든 사람의 기기에서 이미 한 번 끝났으니까요.

이 변환들은 전부 브라우저 안에서 돌아가요. 파일이 서버로 전송되지 않고, 그래서 인코딩 시간도 남의 서버가 아니라 각자의 CPU에 얹혀요. 대신 흡수해 줄 서버가 아예 없는 구조라서요. 저희가 고른 교환 조건이 그거고, 그냥 “빠릅니다”라고 해두고 나중에 알게 하는 것보다 실제로 잰 시간을 적어두는 편이 나아 보였어요.