문서가 백만 건이면 벡터 데이터베이스부터 찾아보는 게 요즘 흔한 순서입니다. 그런데 검색엔진 컨설턴트 더그 턴불은 그 전에 numpy 한 줄을 먼저 시도해보라고 말합니다.

턴불은 2000년대 임베디드 C 코드를 짜던 시절 배운 원칙을 다시 꺼냅니다. 마이크로소프트 개발자 레이먼드 첸이 남긴 말, “내 O(n) 알고리즘이 당신의 O(log n) 알고리즘을 가볍게 이길 수 있다”입니다. 정렬 알고리즘에서 통했던 이 원칙이 벡터 검색에서도 그대로 통한다는 게 그의 주장입니다.
출처: Just brute force your embeddings – Doug Turnbull
벡터DB 없이, 코드 한 줄로
턴불이 실제로 측정한 수치는 이렇습니다. 384차원 임베딩 100만 개를 대상으로, 자신의 맥북(M4) 한 대에서 검색 스레드 하나로 초당 79.7건, 스레드 10개로는 초당 170.5건을 처리했죠. 884만 건 규모로 늘려도 스레드 하나에 초당 9.34건, 스레드 10개에 초당 18.34건이 나왔습니다. 지연 시간은 100만 건 기준 12~58밀리초 수준입니다.
이 성능을 낸 코드는 딱 한 줄입니다. 쿼리 벡터와 전체 문서 벡터 배열을 내적(dot product) 연산 한 번으로 곱하는 것뿐이죠. 별도의 인덱스도, 근사 검색 알고리즘도 없이 모든 문서를 순서대로 훑는 방식인데도 이 정도 속도가 나옵니다.
왜 벡터DB부터 찾을 필요가 없나
턴불이 이 글을 쓴 이유는 실제로 만나는 팀들 상당수가 벡터DB의 복잡도를 감당할 필요가 없기 때문입니다. 문서 100만 건 안팎에, 쿼리 트래픽이 많지 않고, 임베딩을 미리 한 번에 다 써놓는 구조라면 수백만 원짜리 벡터DB를 도입하거나 이를 운영하는 법을 6개월씩 배울 이유가 없는 것이죠.
기준은 명확합니다. 데이터 규모가 감당할 수 있는 수준이라면 브루트포스로 버틸 수 있을 때까지 버티라는 겁니다. 검색 전문가 조 크리스티안 베르굼도 비슷한 말을 남긴 적이 있습니다. “전수 검색만으로 충분한 경우가 생각보다 많다”는 것이죠. 그 규모를 넘어서는 시점이 오면, 그때 데이터베이스 도입을 고민하거나 FAISS 같은 라이브러리로 전체를 메모리에 올려두는 정도로도 충분할 수 있습니다.
턴불은 이 측정치가 numpy의 기본 연산만 쓴 것이라 더 빠르게 만들 여지도 있다고 덧붙입니다. 스레드마다 쿼리를 여러 개씩 몰아주거나, 상위 결과만 추려내는 방식을 검색 과정에 더 정교하게 넣으면 처리량을 더 끌어올릴 수 있다는 것입니다.

답글 남기기