콘텐츠로 이동

AutoEQ 벤치마크

modernGraphTool의 AutoEQ는 AutoEq의 최적화 엔진을 WebAssembly로 옮긴 turboEQ로 실행됩니다. 이 페이지는 turboEQ를 알고리즘으로서 이전에 쓰이던 엔진들, 그리고 원본인 AutoEq와 비교합니다. 모든 엔진이 같은 제약 조건으로 실제 인이어 이어폰 측정 176개를 처리했습니다.

  • 100배 이상 빠릅니다. 8밴드에서 turboEQ의 실행 시간 중앙값은 6.2 ms입니다. CrinGraph는 807 ms, modernGraphTool의 이전 TypeScript 엔진은 1.41초가 걸립니다.
  • 요청한 밴드 개수를 항상 그대로 돌려줍니다. 8밴드를 요청했을 때 정확히 8개를 돌려준 비율은 CrinGraph가 87%, 이전 엔진이 22%였습니다.
  • 8밴드 이상에서는 완전 일치가 가장 가깝게 맞춥니다. 10밴드에서 20 Hz–20 kHz 오차 중앙값은 0.48 dB로, 이전 엔진 0.74, CrinGraph 1.24보다 낮습니다. 8밴드에서는 차이가 작지만(0.77 대 0.82), 나쁜 경우가 덜 나쁩니다(90번째 백분위수 1.59 대 1.90). 헤드폰에서는 모든 밴드 개수에서 앞섭니다.
  • 인이어 5밴드에서는 이전 엔진의 점수가 더 좋은데, 대부분 레벨 덕분입니다. 이전 엔진은 1.16 dB, 완전 일치는 1.27 dB입니다. 이유는 turboEQ가 뒤지는 부분에 있습니다.
  • 고음 보존은 AutoEq와 가깝습니다. 같은 제약 조건에서 둘 다 수렴할 때까지 실행하면 EQ 차이는 중앙값 0.17 dB입니다. 참고로 AutoEq는 조기 종료 여부만으로도 0.07 dB 달라집니다.

모든 엔진에 밴드마다 같은 제한을 걸었습니다.

제한값
Q0.1–10
게인−20 ~ +20 dB
밴드 위치20 Hz–20 kHz
전체 최대 부스트20 dB

방식 자체의 차이는 그대로 두었습니다.

  • CrinGraph에는 셸프 필터가 없어 모든 밴드가 피킹 필터입니다. 나머지 엔진은 밴드가 4개 이상이면 로우·하이 셸프를 하나씩 쓰고, 위치도 직접 정합니다. turboEQ와 AutoEq는 셸프의 Q를 0.4–0.7 사이로 유지합니다.
  • 고음 보존은 10 kHz 위에 밴드를 두지 않습니다. 그 위로는 모양이 아니라 전체 레벨만 평가하기 때문입니다.
  • 전체 부스트 제한은 EQ 전체를 제한하는 AutoEq 계열에만 의미가 있습니다. CrinGraph와 TypeScript 엔진은 밴드마다 따로 제한합니다.

아래의 밴드 개수는 셸프를 포함한 전체 개수입니다. 10밴드는 AutoEq의 8_PEAKING_WITH_SHELVES 프리셋과 같은 구성입니다.

한 번 실행에 걸린 시간의 중앙값(ms)입니다. 마지막 열은 10밴드의 90번째 백분위수입니다.

엔진5밴드8밴드10밴드10밴드, p90
CrinGraph4828079931,080
mGT TypeScript5981,4102,1002,440
turboEQ · 완전 일치1.86.21224
turboEQ · 고음 보존2.18.21939
AutoEq (Python)3074124180

8밴드에서 turboEQ는 CrinGraph보다 약 130배, 이전 엔진보다 약 230배 빠릅니다. 곡선 처리와 맞춤을 합친, AutoEq와 같은 작업에서 고음 보존은 Python 원본보다 약 9배 빠릅니다.

CrinGraph는 페이지의 메인 스레드에서 실행되므로 계산하는 동안 페이지가 멈춥니다. modernGraphTool은 AutoEQ를 웹 워커에서 실행하며, TypeScript 대체 엔진도 마찬가지입니다.

EQ를 적용한 곡선이 타깃에 얼마나 가까운지를, 전체 레벨 차이를 뺀 RMS 오차(dB)로 나타냈습니다. 낮을수록 좋습니다. EQ를 적용하지 않았을 때의 오차 중앙값은 20 Hz–20 kHz에서 3.03 dB, 10 kHz 이하에서 2.23 dB입니다.

20 Hz–20 kHz, 중앙값이며 괄호 안은 90번째 백분위수입니다.

엔진5밴드8밴드10밴드
CrinGraph2.01 (3.04)1.47 (2.84)1.24 (2.60)
mGT TypeScript1.16 (2.28)0.82 (1.90)0.74 (1.85)
turboEQ · 완전 일치1.27 (2.26)0.77 (1.59)0.48 (1.13)
turboEQ · 고음 보존1.72 (2.54)1.63 (2.43)1.63 (2.38)
AutoEq (Python)1.74 (2.52)1.69 (2.47)1.70 (2.45)

10 kHz 이하, 중앙값입니다. AutoEq가 일부러 모양을 맞추지 않는 구간을 뺀 값입니다.

엔진5밴드8밴드10밴드
CrinGraph0.810.370.27
mGT TypeScript0.670.490.44
turboEQ · 완전 일치0.840.450.27
turboEQ · 고음 보존0.880.790.78
AutoEq (Python)0.920.810.80

요청한 밴드 개수를 그대로 돌려준 비율:

엔진5밴드8밴드10밴드
CrinGraph94%87%64%
mGT TypeScript60%22%6%
turboEQ (두 방식 모두)와 AutoEq100%100%100%

CrinGraph는 후보가 되는 피크와 딥이 떨어지면 탐색을 멈춥니다. TypeScript 엔진은 효과가 적다고 판단한 밴드도 제거하므로, 위 결과는 요청보다 적은 밴드로 얻은 경우가 많습니다.

고음 보존의 오차가 큰 것은 의도된 결과입니다. 고음역을 스무딩하고, 10 kHz 이상은 레벨만 평가하며, 그 위에는 밴드를 두지 않습니다. 그래프와의 일치를 내주는 대신 고음역 측정을 믿기 어려울 때도 유효한 보정을 얻습니다.

완전 일치는 TypeScript 엔진처럼, 맞는다면 좁고 깊은 밴드도 씁니다. 그런 밴드가 실제로 들리는 개선인지는 별개의 문제입니다. 여기서 오차는 EQ를 맞춘 바로 그 측정으로 잰 값이고, 고음역의 좁은 특징은 착용 상태에 따라 움직이므로 하나에 정확히 맞춘 밴드가 실제 귀에서는 빗나갈 수 있습니다.

인이어 5밴드에서 이전 TypeScript 엔진은 1.16 dB, 완전 일치는 1.27 dB입니다. 이 차이는 거의 모두 레벨에서 옵니다. 이 페이지의 점수는 전체 레벨 차이를 빼고 계산하는데, 이는 두 곡선을 가장 유리하게 맞춰 주는 방식이고 TypeScript 엔진의 결과는 여기에 기댑니다. AutoEq처럼 EQ 전의 100 Hz–10 kHz 평균 오차로 레벨을 맞추면 TypeScript 엔진은 1.74 dB가 되고, 완전 일치는 그대로 1.27 dB입니다. turboEQ는 레벨을 고정한 채 맞추므로 점수가 달라지지 않습니다.

남은 차이는 최적화 자체에서 옵니다. 밴드가 적으면 turboEQ가 AutoEq의 init()이 정한 시작 배치에서 국소 최솟값에 머무를 때가 있습니다. turboEQ 자신의 목적 함수로 평가해도 TypeScript 엔진의 결과가 더 나은 경우가 5밴드에서 176개 중 48개, 8밴드에서 16개, 10밴드에서 1개입니다.

같은 측정과 같은 제약 조건에서 각 엔진의 EQ가 AutoEq 원본의 결과와 얼마나 다른지를, 20 Hz–20 kHz에서 두 EQ 곡선의 RMS 차이(레벨 차이 제외)로 나타냈습니다. 중앙값이며, 괄호 안은 90번째 백분위수입니다.

엔진8밴드10밴드
CrinGraph1.32 (2.13)1.33 (2.13)
mGT TypeScript1.39 (1.97)1.42 (1.96)
turboEQ · 완전 일치1.39 (2.03)1.57 (2.10)
turboEQ · 고음 보존0.22 (0.52)0.24 (0.53)
AutoEq, 수렴할 때까지 실행0.07 (0.24)0.08 (0.21)

마지막 행은 AutoEq를 프리셋의 조기 종료(min_std)가 있을 때와 없을 때 비교한 값입니다. turboEQ는 항상 수렴할 때까지 실행합니다. 수렴한 AutoEq와 비교하면 고음 보존은 8밴드에서 0.17 dB, 10밴드에서 0.19 dB 차이입니다. 셸프를 AutoEq 프리셋처럼 고정하면 같은 비교에서 0.10 dB였습니다. 셸프가 움직일 수 있으면 두 최적화 엔진이 서로 다른 곳에 자리 잡을 여지가 생깁니다.

완전 일치가 다른 엔진만큼 AutoEq에서 떨어져 있는 것은 의도된 결과입니다. 그래프에 보이는 곡선이라는 다른 목표를 맞추기 때문입니다.

헤드폰 데이터는 작습니다. 헤드폰 두 개의 곡선 아홉 개이고, 대부분은 한 헤드폰의 EQ 모드별 측정입니다. 별도의 결과라기보다 일관성 확인으로 봐 주세요. 8밴드 결과입니다.

엔진시간 중앙값개수 일치20 Hz–20 kHz10 kHz 이하
CrinGraph947 ms100%3.220.87
mGT TypeScript1.48 s22%1.380.96
turboEQ · 완전 일치5.8 ms100%1.260.82
turboEQ · 고음 보존6.8 ms100%1.670.90
AutoEq (Python)99 ms100%1.881.03

5밴드와 10밴드에서도 전 대역에서 완전 일치가 앞섭니다(1.60 대 1.81, 1.14 대 1.31). 이 헤드폰들의 고음역은 좁은 피크와 노치가 이어진 모양인데, 완전 일치 모드에서는 가파른 밴드에 대한 페널티와 AutoEq가 타깃에 더하는 스무딩이 빠져 이를 따라갈 수 있습니다.

CrinGraphmGT TypeScript (대체 엔진)modernGraphTool의 turboEQAutoEq (Python)
최적화 방식후보 탐색 후 격자 위에서 미세 조정CrinGraph 계열, 셸프·가지치기 추가모든 파라미터를 해석적 기울기로 한 번에모든 파라미터를 SciPy SLSQP로 한 번에
필터 종류피킹피킹, 로우·하이 셸프피킹, 위치까지 최적화하는 로우·하이 셸프피킹, 셸프 (프리셋은 위치 고정)
요청한 밴드 개수종종 부족종종 부족항상 일치항상 일치
범위맞춘 뒤 잘라냄탐색 범위를 제한최적화 안의 경계최적화 안의 경계
날카로운 필터허용허용완전 일치: 허용. 고음 보존: 페널티옥타브당 약 18 dB 이상에 페널티
레벨 정렬그래프 정규화 결과 그대로1 kHz에 고정100 Hz–10 kHz 평균 오차 최소화100 Hz–10 kHz 평균 오차 최소화
고음역첫 단계는 7 kHz 이상 제외다른 대역과 같이 맞춤완전 일치: 모양 전체. 고음 보존: AutoEq 방식스무딩, 10 kHz 이상은 레벨만
부스트 제한밴드별밴드별EQ 전체, 게인 범위의 최댓값EQ 전체, 기본 6 dB
그래픽 EQ 프리셋없음없음지원, 프리셋 밴드의 게인만 최적화지원 (고정 밴드 EQ)
실행 위치페이지 메인 스레드웹 워커웹 워커, WebAssembly (gzip 약 37 KB)Python
  • 데이터. 한 squig.link 데이터베이스의 측정입니다. 기기마다 측정 하나, phone_book.json 항목의 첫 파일을 쓰고 좌우를 평균했습니다. 인이어 곡선은 176개입니다. 헤드폰은 수가 적어 곡선 아홉 개를 모두 썼습니다. 타깃은 인이어에 Harman IE 2019v2, 헤드폰에 Harman 2018을 썼습니다.
  • 입력. 각 엔진에는 원래 앱이 준비하는 방식 그대로 곡선을 넣었습니다. CrinGraph는 1/48 옥타브 격자와 60 phon 라우드니스 정규화, modernGraphTool은 1/48 옥타브 스무딩과 500 Hz 정규화, AutoEq는 명령줄 도구처럼 가공하지 않은 평균 곡선입니다.
  • 평가. 모든 엔진의 필터를 같은 48 kHz 바이쿼드로 계산해 가공하지 않은 측정에 더하고, 1/48 옥타브 격자에서 가공하지 않은 타깃과 비교했습니다.
  • 시간. 워밍업 1회 뒤, 한 번에 하나씩 엔진 호출 시간만 쟀습니다. JavaScript 엔진은 Chrome과 같은 V8 엔진인 Node 24.18에서 실행했습니다. 앱에서는 워커와 메시지를 주고받는 시간이 더해집니다. AutoEq는 Python 3.14.7, NumPy 2.5.3, SciPy 1.18.1에서 실행했습니다.
  • 버전. turboEQ 0.2.0, squiglink/lab main(9ff842c)의 CrinGraph, AutoEq v4.1.2.
  • 장비. Mac mini, Apple M4, 16 GB, macOS 27.2. 2026-09-25에 측정했습니다.

시간 값은 다른 장비에서 그대로 재현되지 않습니다. 행 사이의 비율은 비슷하게 유지되어야 합니다.

측정 스크립트는 scripts/bench-autoeq/에 있습니다. squiglink/lab 체크아웃, AutoEq 체크아웃, 그리고 phone_book.json이 있는 측정 폴더가 필요합니다. 제약 조건은 위의 값이 기본이며 --q, --gain, --fc로 바꿀 수 있습니다.

터미널 창
node scripts/bench-autoeq/run.mjs --data DATA_DIR --target TARGET.txt \
--lab LAB_REPO --out iem.jsonl
python scripts/bench-autoeq/upstream.py --autoeq AUTOEQ_REPO \
--curves iem.curves.json --out iem-upstream.jsonl
node scripts/bench-autoeq/score.mjs --curves iem.curves.json iem.jsonl iem-upstream.jsonl

upstream.py는 iem.curves.json에서 제약 조건을 읽으므로 AutoEq도 같은 조건으로 실행됩니다. AutoEq가 모두 불러오므로 NumPy, SciPy, Matplotlib, tabulate, PyYAML이 필요합니다.