본문 바로가기
C.W.K.
Stream
Lesson 09 of 10 · published

거짓말 안 하는 벤치마크

~12 min · benchmarking, performance, measurement

Level 0Scout
0 XP0/80 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

벤치마크가 거짓말하게 만드는 3 가지

인터넷에 굴러다니는 가벼운 SQLite 벤치마크는 거의 다 아래 셋 중 하나 때문에 틀렸어. 네가 잴 때는 피해.

  1. 차가운 cache와 따뜻한 cache — 첫 실행은 디스크를 읽고, 그다음부터는 OS page cache에서 가져와. 어느 쪽을 재는 건지 먼저 정하고, 의도적으로 데우거나 식혀.
  2. statement마다 transaction — autocommit으로 쓰면 row마다 fsync가 한 번씩 걸려. transaction 없이 10k INSERT를 도는 루프는 SQLite의 write 속도를 재는 게 아니라, 파일시스템 sync latency를 재는 거야.
  3. 너무 작은 dataset — row가 100개면 어느 DB든 빨라. 현실적인 row 수로 재. 이왕이면 production 규모의 몇 배로.
Principle: 어떤 PRAGMA를 걸었는지, dataset이 어떤 모양인지, cache가 따뜻했는지가 안 적힌 벤치마크는 벤치마크가 아니야. 이 셋은 항상 같이 적어.

Code

제대로 잰 Python micro-benchmark·python
import sqlite3, time, os, statistics

def setup():
    if os.path.exists('bench.db'):
        os.remove('bench.db')
    conn = sqlite3.connect('bench.db')
    conn.execute('PRAGMA journal_mode = WAL')
    conn.execute('PRAGMA synchronous = NORMAL')
    conn.execute('CREATE TABLE t(id INTEGER PRIMARY KEY, v TEXT)')
    return conn

def bench_inserts(n: int) -> float:
    conn = setup()
    rows = [(f'v{i}',) for i in range(n)]
    start = time.perf_counter()
    with conn:
        conn.executemany('INSERT INTO t(v) VALUES (?)', rows)
    return time.perf_counter() - start

durations = [bench_inserts(10000) for _ in range(5)]
print(f'10k inserts: median {statistics.median(durations)*1000:.1f} ms')

External links

Exercise

벤치마크 도구를 하나 짜봐. 세 가지를 재는 거야. executemany와 transaction을 쓸 때와 row마다 autocommit할 때의 bulk insert 처리량, cache가 차가울 때와 따뜻할 때의 단건 read 처리량, 그리고 인덱스가 있을 때와 없을 때의 같은 단건 read. 걸어둔 PRAGMA와 dataset 모양을 전부 문서에 남기고, 스크립트는 누가 돌려도 같은 결과가 나오게 만들어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.