문서 번호: 11-4260033
Document Information
•
최초 작성일 : 2026.02.04
•
최종 수정일 : 2026.02.04
•
이 문서는 아래 버전을 기준으로 작성되었습니다.
◦
Singlestore : 9.0.18
◦
Rust: 1.93.0
Goal
Rust 기반 Wasm UDF를 사용하여 SingleStore에서 Hamming distance를 구현합니다.
Hamming Distance 개요
벡터 검색에서 검색 성능과 정확도를 최적화 하려면 적절한 distance metric을 선택하는 것이 중요합니다. Euclidean distance와 Cosine distance가 일반적으로 사용되지만, 데이터 표현 방식과 사용 사례에 따라 Hamming distance가 고유한 이점을 제공합니다.
Hamming distance는 동일한 길이를 가진 두 binary vector 간에 서로 다른 bit의 개수를 계산합니다.
이 metric은 크기 차이(numerical distance)보다 bit mismatch(비트의 불일치)가 중요한 경우에 적합합니다.
주요 응용 분야로는 텍스트 처리, 코드의 오류 검출, DNA 서열 분석 등이 있습니다. 부동소수점 연산이 필요한 Euclidean distance나 cosine distance와 달리, Hamming distance는 binary 값에 대해 연산을 수행하므로 연산 비용이 더 낮습니다.
동작 방법
Hamming distance는 두 binary vector의 각 byte 쌍에 XOR(Exclusive OR) 연산을 수행한 후, 서로 다른 bit의 개수를 계산(popcount)하여 구합니다. 총 count는 두 vector가 얼마나 다른지를 나타냅니다.
Hamming Distance 예시
다음은 두 개의 4-byte(32-bit) vector를 비교하는 예시입니다.
•
vector 1 (FFFFAFDF) = 11111111 11111111 10101111 11011111
•
vector 2 (ABCCFFDF) = 10101011 11001100 11111111 11011111
**예시 1:**
vector1 (hex) : F F F F A F D F
vector1 (bin) : 1111 1111 | 1111 1111 | 1010 1111 | 1101 1111
vector2 (hex) : A B C C F F D F
vector2 (bin) : 1010 1011 | 1100 1100 | 1111 1111 | 1101 1111
-------------------------------------------------------------
XOR result : 0101 0100 | 0011 0011 | 0101 0000 | 0000 0000
Bit diff : (3) | (4) | (2) | (0)
-------------------------------------------------------------
Hamming Distance = 9 (3 + 4 + 2 + 0)
**예시 2:**
Query1 (hex) : FF FF AF DF
Query1 (bin) : 1111 1111 | 1111 1111 | 1010 1111 | 1101 1111
Query2 (hex) : FF FF AF DF
Query2 (bin) : 1111 1111 | 1111 1111 | 1010 1111 | 1101 1111
-------------------------------------------------------------
XOR result : 0000 0000 | 0000 0000 | 0000 0000 | 0000 0000
Bit diff : (0) | (0) | (0) | (0)
-------------------------------------------------------------
Hamming Distance = 0 (0 + 0 + 0 + 0)
Plain Text
복사
참고: 두 vector가 동일하면 XOR 결과가 모두 0이 되어 Hamming distance는 0이 됩니다. distance가 낮을수록 유사도가 높습니다.
SingleStore Hamming distance
SingleStore의 기본 vector 함수는 Euclidean distance, cosine distance와 같은 일반적인 distance metric을 지원하지만, Hamming distance나 bit vector type은 지원하지 않습니다. 이 제한을 해결하기 위해 binary vector를 BLOB으로 저장하고 Wasm UDF를 사용하여 Hamming distance를 계산할 수 있습니다.
구현 가이드
Step 1: Rust 설치
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ rustc --version
-bash: rustc: command not found
# 세션을 재시작한 후 설치 확인
$ exec $SHELL
$ rustc --version
rustc 1.93.0 (254b59607 2026-01-19)
$ cargo --version
cargo 1.93.0 (083ac5135 2025-12-15)
# WebAssembly target 및 wit-bindgen CLI 추가
$ rustup target add wasm32-wasip1
$ cargo install --git https://github.com/bytecodealliance/wit-bindgen wit-bindgen-cli
Bash
복사
Step 2: 프로젝트 구조 생성
•
새로운 Rust library 프로젝트를 초기화
$ cargo new hamming_wasm
$ ls hamming_wasm
Cargo.toml src
Bash
복사
Step 3: Cargo.toml 설정
•
wit-bindgen dependency를 추가
$ vi hamming_wasm/Cargo.toml
[package]
name = "hamming_distance"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen.git", rev = "60e3c5b41e616fee239304d92128e117dd9be0a7" }
TOML
복사
Step 4: WIT Interface 정의 (hamming.wit)
SingleStore에서 Wasm module을 호출할 때 사용할 함수 signature를 정의합니다.
이 함수는 byte_length parameter와 두 개의 binary vector를 byte array로 받습니다.
byte_length를 조정하여 전체 byte를 비교(full comparison)하거나 처음 N byte만 비교(partial comparison)할 수 있습니다.
$ vi hamming_wasm/hamming.wit
hamming-distance: func(byte-length: s32, a: list<u8>, b: list<u8>) -> s32
Bash
복사
Step 5: Rust 소스 코드 구현 (src/lib.rs)
이 코드는 입력 길이를 검증하고, 각 byte 쌍에 XOR 연산을 수행한 후, 서로 다른 bit 개수를 계산하고 총합을 반환합니다.
$ vi hamming_wasm/src/lib.rs
wit_bindgen_rust::export!("hamming.wit");
struct Hamming;
impl hamming::Hamming for Hamming {
fn hamming_distance(byte_length: i32, a: Vec<u8>, b: Vec<u8>) -> i32 {
let num_bytes = byte_length as usize;
if a.len() < num_bytes || b.len() < num_bytes {
return -1;
}
// XOR each byte pair, count the different bits, sum total
let distance: u32 = a.iter()
.zip(b.iter())
.take(num_bytes)
.map(|(&x, &y)| (x ^ y).count_ones())
.sum();
distance as i32
}
}
Rust
복사
Step 6: Rust 코드를 WebAssembly로 컴파일
프로젝트를 컴파일합니다.
컴파일된 module은 target/wasm32-wasip1/release/hamming_distance.wasm에 생성됩니다.
$ cd hamming_wasm
$ cargo build --target wasm32-wasip1 --release --lib
Compiling proc-macro2 v1.0.106
Compiling pulldown-cmark v0.8.0
Compiling tinyvec_macros v0.1.1
Compiling anyhow v1.0.100
Compiling tinyvec v1.10.0
Compiling unicode-ident v1.0.22
Compiling quote v1.0.44
Compiling bitflags v1.3.2
Compiling unicase v2.9.0
Compiling memchr v2.7.6
Compiling unicode-normalization v0.1.25
Compiling id-arena v2.3.0
Compiling unicode-xid v0.2.6
Compiling unicode-segmentation v1.12.0
Compiling syn v1.0.109
Compiling wit-bindgen-gen-rust-wasm v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling heck v0.3.3
Compiling syn v2.0.114
Compiling wit-parser v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling wit-bindgen-gen-core v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling wit-bindgen-gen-rust v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling wit-bindgen-rust-impl v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling async-trait v0.1.89
Compiling wit-bindgen-rust v0.1.0 (https://github.com/bytecodealliance/wit-bindgen.git?rev=60e3c5b41e616fee239304d92128e117dd9be0a7#60e3c5b4)
Compiling hamming_distance v0.1.0 (/home/opc/hamming_wasm)
Finished `release` profile [optimized] target(s) in 3.45s
Bash
복사
Step 7: SingleStore에 배포
Wasm module을 배포하기 위한 hamming_func.sh를 생성한다. 이 script는 컴파일된 .wasm 파일을 읽어 base64로 인코딩한 후 CREATE FUNCTION 문을 실행합니다.
vi ./hamming_func.sh
#!/bin/bash
HOST="localhost"
USER="root"
PASSWORD="1"
DB="db1"
WASM_FILE="~/hamming_wasm/target/wasm32-wasip1/release/hamming_distance.wasm"
# Generate base64 using openssl
BASE64_DATA=$(openssl base64 -A -in "$WASM_FILE")
# Execute SQL
singlestore -h "$HOST" -u "$USER" -p"$PASSWORD" "$DB" <<EOF
CREATE OR REPLACE FUNCTION hamming_distance(
byte_length INT NOT NULL,
a BLOB NOT NULL,
b BLOB NOT NULL
)
RETURNS INT NOT NULL
AS WASM
ABI CANONICAL
FROM BASE64 '$BASE64_DATA'
USING EXPORT 'hamming-distance';
EOF
echo "Function Created"
Bash
복사
•
배포 후 함수가 생성되었는지 확인
# SingleStore 실행
$ use db1;
singlestore> show functions;
+------------------+-----------------------+---------+-------------+--------------+------+---------+
| Functions_in_db1 | Function Type | Definer | Data Format | Runtime Type | Link | Options |
+------------------+-----------------------+---------+-------------+--------------+------+---------+
| hamming_distance | User Defined Function | root@% | | Wasm | | |
+------------------+-----------------------+---------+-------------+--------------+------+---------+
1 row in set (0.00 sec)
SQL
복사
사용 예시
테이블 설정
•
Binary vector 데이터를 BLOB으로 저장하는 테이블을 생성
singlestore>
create table t1 (
id int,
v blob not null
);
--테스트 vector Insert(각 hex string = 4 bytes = 32 bits)
singlestore>
insert into t1 (id, v) values
(1, unhex('FFFFFFFF')),
(2, unhex('ABCCFFDF')),
(3, unhex('FF00FF00')),
(4, unhex('FFFFAFDF'));
Query OK, 3 rows affected (0.03 sec)
SQL
복사
Case 1: Full Vector Comparison
•
query vector와 전체 4 byte를 비교하여 가장 유사한 항목 찾기
singlestore>
set @query_vec = unhex('FFFFAFDF');
singlestore>
select
id,
hamming_distance(4, v, @query_vec) as dist
from
t1
order by
dist asc;
+------+------+
| id | dist |
+------+------+
| 4 | 0 | -- 가장 유사함
| 1 | 3 |
| 2 | 9 |
| 3 | 17 | -- 가장 유사하지 않음
+------+------+
4 rows in set (0.03 sec)
SQL
복사
Case 2: Partial Vector Comparison
•
byte_length parameter를 사용하여 vector의 일부 byte만 비교할 수 있습니다.
•
아래 예시에서는 각 vector의 2번째 byte부터 2 byte만 추출하여 비교했습니다.
참고: 이 예시에서는 간단하게 substring을 사용하였으나, Wasm 코드를 확장하여 offset을 내부에서 처리할 수도 있습니다. use case에 따라 성능 trade-off를 고려하여 선택하시면 됩니다.
singlestore> set @query_vec = unhex('CCFF');
singlestore>
select
id,
hamming_distance(2, substring(v, 2, 2), @query_vec) AS dist
from
t1
order by
dist asc;
+------+------+
| id | dist |
+------+------+
| 2 | 0 |
| 3 | 4 |
| 1 | 4 |
| 4 | 6 |
+------+------+
4 rows in set (0.00 sec)
SQL
복사
References
History
일자 | 작성자 | 비고 |
2026.02.04 | mike | 최초작성 |

