가상화 벤치마크
랩은 목록, 흐름 격자, 메이슨리, 트랙 격자, 자유 좌표 배치를 동등한 공개 API 조건으로 측정합니다. 같은 family 안에서는 모든 어댑터에 동일한 10만 개 결정적 항목을 주고 초기 렌더, 스크롤 반응, 삽입·이동·삭제·크기 변경 뒤 안정화 시간을 확인합니다.
목록
흐름 격자
이 조건을 지원하지 않거나 측정값이 없습니다.
원본 측정값 보기
초기 렌더와 스크롤
| 라이브러리 | 입력 방식 | 설정 | 첫 항목 | 안정 레이아웃 | 스크롤 중앙값 | 스크롤 p95 | 표본 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | 19.2 ms | 19.2 ms | 19.5 ms | 3 ms | 4 ms | 100 |
| Sectile Virtual | estimated | 14.6 ms | 14.6 ms | 14.8 ms | 2.8 ms | 3.6 ms | 100 |
| Sectile Virtual | automatic | 16.3 ms | 16.3 ms | 16.5 ms | 1.9 ms | 2.4 ms | 100 |
| React Virtuoso | automatic | 0.9 ms | 11.9 ms | 12 ms | 0.5 ms | 0.7 ms | 100 |
레이아웃 변경
| 라이브러리 | 입력 방식 | 작업 | 위치 | 스크롤 중앙값 | p95 | 표본 | 실패 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | insert | start | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | middle | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | end | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | fixed | move | start | 8 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | fixed | move | middle | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | fixed | move | end | 8 ms | 9 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | start | 8.1 ms | 9 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | middle | 8.1 ms | 9.2 ms | 50 | 0 |
| Sectile Virtual | fixed | remove | end | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | start | 8.2 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | automatic | insert | start | 8.2 ms | 9.1 ms | 30 | 0 |
| React Virtuoso | automatic | insert | start | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | middle | 7.9 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | automatic | insert | middle | 8 ms | 8.9 ms | 30 | 0 |
| React Virtuoso | automatic | insert | middle | 8.2 ms | 8.8 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | end | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | automatic | insert | end | 8.1 ms | 8.5 ms | 30 | 0 |
| React Virtuoso | automatic | insert | end | 8.2 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | estimated | move | start | 8.1 ms | 9 ms | 30 | 0 |
| Sectile Virtual | automatic | move | start | 8.2 ms | 8.3 ms | 30 | 0 |
| React Virtuoso | automatic | move | start | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | estimated | move | middle | 8 ms | 8.9 ms | 30 | 0 |
| Sectile Virtual | automatic | move | middle | 8.1 ms | 9 ms | 30 | 0 |
| React Virtuoso | automatic | move | middle | 8.2 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | estimated | move | end | 8.1 ms | 9 ms | 30 | 0 |
| Sectile Virtual | automatic | move | end | 8.1 ms | 9 ms | 30 | 0 |
| React Virtuoso | automatic | move | end | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | start | 8.2 ms | 8.4 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | start | 8.2 ms | 8.3 ms | 30 | 0 |
| React Virtuoso | automatic | remove | start | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | middle | 8.1 ms | 8.8 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | middle | 8 ms | 8.4 ms | 30 | 0 |
| React Virtuoso | automatic | remove | middle | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | end | 8.1 ms | 8.9 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | end | 8.1 ms | 8.3 ms | 30 | 0 |
| React Virtuoso | automatic | remove | end | 8.2 ms | 8.9 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | start | 8 ms | 8.8 ms | 30 | 0 |
| Sectile Virtual | automatic | resize | start | 7.7 ms | 9.3 ms | 30 | 0 |
| React Virtuoso | automatic | resize | start | 8.2 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | middle | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | automatic | resize | middle | 8.1 ms | 8.6 ms | 30 | 0 |
| React Virtuoso | automatic | resize | middle | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | end | 8.1 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | automatic | resize | end | 8.2 ms | 8.3 ms | 30 | 0 |
| React Virtuoso | automatic | resize | end | 8.1 ms | 8.7 ms | 30 | 0 |
메이슨리
이 조건을 지원하지 않거나 측정값이 없습니다.
원본 측정값 보기
초기 렌더와 스크롤
| 라이브러리 | 입력 방식 | 설정 | 첫 항목 | 안정 레이아웃 | 스크롤 중앙값 | 스크롤 p95 | 표본 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | 15.2 ms | 15.2 ms | 15.4 ms | 2.8 ms | 3.5 ms | 100 |
| Sectile Virtual | estimated | 9.6 ms | 9.6 ms | 9.9 ms | 6.4 ms | 8.9 ms | 100 |
| Sectile Virtual | automatic | 15.8 ms | 15.8 ms | 16.1 ms | 6.1 ms | 21 ms | 100 |
| TanStack Virtual | estimated | 18 ms | 18 ms | 18.2 ms | 0.4 ms | 0.6 ms | 100 |
레이아웃 변경
| 라이브러리 | 입력 방식 | 작업 | 위치 | 스크롤 중앙값 | p95 | 표본 | 실패 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | insert | start | 8.1 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | middle | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | end | 8.1 ms | 9 ms | 30 | 0 |
| Sectile Virtual | fixed | move | start | 8.1 ms | 8.9 ms | 30 | 0 |
| Sectile Virtual | fixed | move | middle | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | fixed | move | end | 8.1 ms | 9.1 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | start | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | middle | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | end | 8.1 ms | 8.2 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | start | 81.4 ms | 134 ms | 30 | 0 |
| Sectile Virtual | automatic | insert | start | 68.4 ms | 138.9 ms | 30 | 0 |
| TanStack Virtual | estimated | insert | start | 8.3 ms | 9.6 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | middle | 43.2 ms | 74.8 ms | 40 | 0 |
| Sectile Virtual | automatic | insert | middle | 51.1 ms | 85.3 ms | 30 | 0 |
| TanStack Virtual | estimated | insert | middle | 8.6 ms | 11.1 ms | 30 | 0 |
| Sectile Virtual | estimated | insert | end | 7 ms | 7.8 ms | 30 | 0 |
| Sectile Virtual | automatic | insert | end | 6.8 ms | 7.3 ms | 30 | 0 |
| TanStack Virtual | estimated | insert | end | 8.2 ms | 9.7 ms | 30 | 0 |
| Sectile Virtual | estimated | move | start | 68.3 ms | 136.1 ms | 30 | 0 |
| Sectile Virtual | automatic | move | start | 67.8 ms | 136.2 ms | 30 | 0 |
| TanStack Virtual | estimated | move | start | 8.3 ms | 9.8 ms | 30 | 0 |
| Sectile Virtual | estimated | move | middle | 37.8 ms | 72.7 ms | 30 | 0 |
| Sectile Virtual | automatic | move | middle | 38.9 ms | 72.7 ms | 30 | 0 |
| TanStack Virtual | estimated | move | middle | 8.2 ms | 10.3 ms | 30 | 0 |
| Sectile Virtual | estimated | move | end | 6.9 ms | 8.6 ms | 30 | 0 |
| Sectile Virtual | automatic | move | end | 6.8 ms | 7.2 ms | 30 | 0 |
| TanStack Virtual | estimated | move | end | 8.3 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | start | 73.6 ms | 136.7 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | start | 71.9 ms | 141.3 ms | 30 | 0 |
| TanStack Virtual | estimated | remove | start | 8.3 ms | 10.2 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | middle | 40.4 ms | 76.5 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | middle | 40.1 ms | 71.6 ms | 30 | 0 |
| TanStack Virtual | estimated | remove | middle | 8.3 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | estimated | remove | end | 6.4 ms | 6.8 ms | 30 | 0 |
| Sectile Virtual | automatic | remove | end | 6.6 ms | 7.2 ms | 30 | 0 |
| TanStack Virtual | estimated | remove | end | 8.3 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | start | 3.1 ms | 4.4 ms | 40 | 0 |
| Sectile Virtual | automatic | resize | start | 3.2 ms | 3.7 ms | 30 | 0 |
| TanStack Virtual | estimated | resize | start | 8.3 ms | 9.6 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | middle | 3.1 ms | 3.4 ms | 30 | 0 |
| Sectile Virtual | automatic | resize | middle | 3 ms | 5.9 ms | 30 | 0 |
| TanStack Virtual | estimated | resize | middle | 8.2 ms | 9.3 ms | 30 | 0 |
| Sectile Virtual | estimated | resize | end | 3.2 ms | 4.3 ms | 30 | 0 |
| Sectile Virtual | automatic | resize | end | 3.1 ms | 4.2 ms | 30 | 0 |
| TanStack Virtual | estimated | resize | end | 8.2 ms | 10.2 ms | 30 | 0 |
트랙 격자
이 조건을 지원하지 않거나 측정값이 없습니다.
원본 측정값 보기
초기 렌더와 스크롤
| 라이브러리 | 입력 방식 | 설정 | 첫 항목 | 안정 레이아웃 | 스크롤 중앙값 | 스크롤 p95 | 표본 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | 12.6 ms | 12.6 ms | 13.2 ms | 8.7 ms | 13.5 ms | 100 |
| react-window | fixed | 2.7 ms | 2.7 ms | 3.5 ms | 8 ms | 9.5 ms | 100 |
| Virtua | estimated | 초기 렌더 오류 · geometry:item-37215 | |||||
레이아웃 변경
| 라이브러리 | 입력 방식 | 작업 | 위치 | 스크롤 중앙값 | p95 | 표본 | 실패 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | fixed | insert | start | 11.4 ms | 16.6 ms | 30 | 0 |
| react-window | fixed | insert | start | 7.9 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | middle | 12.4 ms | 17.6 ms | 30 | 0 |
| react-window | fixed | insert | middle | 7.7 ms | 8 ms | 30 | 0 |
| Sectile Virtual | fixed | insert | end | 9.7 ms | 14.3 ms | 30 | 0 |
| react-window | fixed | insert | end | 7.5 ms | 8.4 ms | 30 | 0 |
| Sectile Virtual | fixed | move | start | 9.1 ms | 13.9 ms | 30 | 0 |
| react-window | fixed | move | start | 7.8 ms | 8.8 ms | 50 | 0 |
| Sectile Virtual | fixed | move | middle | 10.1 ms | 15.6 ms | 30 | 0 |
| react-window | fixed | move | middle | 7.5 ms | 7.9 ms | 30 | 0 |
| Sectile Virtual | fixed | move | end | 10.2 ms | 15.6 ms | 30 | 0 |
| react-window | fixed | move | end | 7.5 ms | 10.9 ms | 50 | 0 |
| Sectile Virtual | fixed | remove | start | 11.3 ms | 16.6 ms | 30 | 0 |
| react-window | fixed | remove | start | 7.9 ms | 8.2 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | middle | 12.4 ms | 18.8 ms | 30 | 0 |
| react-window | fixed | remove | middle | 7.4 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | fixed | remove | end | 10.2 ms | 15.5 ms | 30 | 0 |
| react-window | fixed | remove | end | 7.5 ms | 8.7 ms | 30 | 0 |
| Sectile Virtual | fixed | resize | start | 9.6 ms | 14.6 ms | 30 | 0 |
| react-window | fixed | resize | start | 7.9 ms | 8.8 ms | 30 | 0 |
| Sectile Virtual | fixed | resize | middle | 10.7 ms | 16.1 ms | 30 | 0 |
| react-window | fixed | resize | middle | 7.5 ms | 8.3 ms | 30 | 0 |
| Sectile Virtual | fixed | resize | end | 9.5 ms | 14.5 ms | 30 | 0 |
| react-window | fixed | resize | end | 7.5 ms | 8.4 ms | 30 | 0 |
자유 좌표 배치
원본 측정값 보기
초기 렌더와 스크롤
| 라이브러리 | 입력 방식 | 설정 | 첫 항목 | 안정 레이아웃 | 스크롤 중앙값 | 스크롤 p95 | 표본 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | positioned | 90 ms | 90 ms | 90.4 ms | 7.2 ms | 12 ms | 100 |
레이아웃 변경
| 라이브러리 | 입력 방식 | 작업 | 위치 | 스크롤 중앙값 | p95 | 표본 | 실패 |
|---|---|---|---|---|---|---|---|
| Sectile Virtual | positioned | insert | start | 141 ms | 168.5 ms | 30 | 0 |
| Sectile Virtual | positioned | insert | middle | 100.9 ms | 130.8 ms | 30 | 0 |
| Sectile Virtual | positioned | insert | end | 8.2 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | positioned | move | start | 8.1 ms | 9.2 ms | 30 | 0 |
| Sectile Virtual | positioned | move | middle | 8 ms | 9 ms | 30 | 0 |
| Sectile Virtual | positioned | move | end | 8 ms | 9.5 ms | 30 | 0 |
| Sectile Virtual | positioned | remove | start | 139.5 ms | 161.7 ms | 30 | 0 |
| Sectile Virtual | positioned | remove | middle | 98.9 ms | 130.7 ms | 30 | 0 |
| Sectile Virtual | positioned | remove | end | 8 ms | 8.5 ms | 30 | 0 |
| Sectile Virtual | positioned | resize | start | 8.1 ms | 9 ms | 30 | 0 |
| Sectile Virtual | positioned | resize | middle | 8 ms | 9 ms | 30 | 0 |
| Sectile Virtual | positioned | resize | end | 8 ms | 8.6 ms | 30 | 0 |
행 구성을 나눈 이유
같은 높이 실험은 라이브러리의 기본 비용을 비교합니다. 서로 다른 높이 실험은 제목, 본문, 태그, 펼친 상세 내용이 섞인 256가지 행을 사용합니다. 접힌 행만 해도 높이가 8단계로 나뉘며 실제 범위는 71–159px입니다. 행 순서는 고정된 난수 규칙으로 정해져 모든 라이브러리가 같은 데이터를 받습니다.
앱은 행별 높이 계산과 전달을 생략합니다. 각 라이브러리가 화면에 만든 DOM을 직접 재고 다음 배치를 갱신합니다. 러너가 따로 측정한 정답 높이는 판정에만 씁니다.
화면에 나타난 행부터 실제 높이가 확정됩니다. 이질적 높이 실험은 현재 화면의 행 높이, 순서, 겹침, 빈 영역, 스크롤 기준 위치를 엄격하게 판정하고 전체 높이 추정 오차는 별도 수치로 기록합니다.
2차원 레이아웃의 정확값 조건은 전체 콘텐츠 범위와 항목의 절대 좌표까지 판정합니다. 공개 API가 항목별 정확한 크기를 받지만 합산 범위는 지연 계산하는 경우, 절대 항목 기하는 정확하게 판정하고 아직 방문하지 않은 전체 범위만 임시 값으로 허용합니다. 예상값과 DOM 실측 조건은 요청한 데이터 버전의 반영 여부, 유한한 임시 콘텐츠 범위, 렌더된 항목의 식별자와 크기, 비어 있지 않은 화면 영역을 판정합니다. 변경 완료는 삽입·인접 이동·삭제·크기 변경의 대상 항목이 실제 DOM 상태에 반영됐을 때 판정합니다. 목록과 2차원 레이아웃은 같은 대상 배치 및 프레임 안정화 커널을 쓰고, 스냅샷과 오라클 판정만 family별로 나눕니다. 2차원 변경 표본은 observer가 처음으로 정상 좌표를 확인한 시점과 그 다음 프레임에서 다시 확인한 결과를 함께 남깁니다. probe 비용은 별도 기록하고 기존 프레임 결과는 보수적인 비교 점수로 유지합니다. 아직 보이지 않은 항목의 추정값이나 라이브러리별 메이슨리 열 선택은 오류로 취급하지 않으면서, 바깥 프레임워크의 커밋만 끝난 상태를 가상화 작업 완료로 오인하지 않습니다. 흐름 격자와 메이슨리의 고정값 조건은 같은 크기 항목을 사용하며, 크기 변경은 공개 API가 항목 크기 변경을 지원하는 조건에서만 실행합니다.
높이 입력 조건
정확한 높이, 예상 높이, DOM 실측 조건을 각각 측정합니다. 고정 높이 전용 경로와 DOM 실측 경로의 비용을 분리해 비교할 수 있습니다.
- 고정 높이: 앱이 정확한 72px을 전달합니다. 같은 높이 실험에서만 사용합니다.
- 예상값 제공: 앱이 모든 행에 공통으로 쓸 72px을 전달합니다. 라이브러리는 화면에 나타난 행을 DOM에서 다시 잽니다.
- 높이 생략: 앱의 높이 입력을 생략합니다. Sectile은 viewport를 채울 초기 표본을 먼저 만들고 실제 DOM 높이를 잰 뒤, 그 측정값으로 나머지 전체 범위를 구성합니다.
높이 생략 조건은 높이 입력 없이 시작하는 API에서 실행합니다. 입력값이 필요한 라이브러리는 해당 조건에 필요한 값을 표시합니다.
높이 변경 실험에서는 선택한 행의 상세 내용을 펼칩니다. 문장이 추가되면서 생긴 높이 변화는 브라우저 DOM에서 읽습니다.
각 라이브러리가 요구하는 연결 방식은 그대로 따랐습니다. react-virtualized는 측정 캐시를 비우고 목록 배치를 다시 계산하라는 호출이 필요합니다. 나머지 비교 대상은 DOM 크기 변경을 자동으로 감지하는 API를 사용했습니다. 이런 앱 코드의 차이도 실행 시간에 포함됩니다.
목록 변경 실험
삽입·이동·삭제·높이 변경을 목록의 세 지점에서 실행합니다.
- 시작: 첫 번째 행
- 중간: 50,000번째 행을 화면 맨 위에 둔 상태
- 끝: 마지막 행이 보이도록 맨 아래까지 내린 상태
삽입과 삭제, 높이 변경은 해당 지점의 행 하나를 바꿉니다. 이동은 그 행과 바로 옆 행의 순서를 맞바꿉니다. 매번 10만 개의 원본 목록으로 되돌린 뒤 같은 실험을 최대 50회 반복합니다.
시간은 목록 변경을 요청한 순간부터 DOM 순서와 행 높이, 화면을 채운 상태, 기준 행의 위치가 모두 맞는 첫 프레임까지 잽니다. 같은 높이 조건에서는 전체 스크롤 높이도 완료 판정에 포함합니다. 독립 인스턴스별 5·5·10·10·10·10개 배치로 최대 50회 측정하며 중앙값과 p95가 안정되면 30개 이후 조기 종료합니다. 표본 사이에는 원본 목록과 화면 배치를 확인한 뒤 다음 변경을 시작합니다. 복원 검사를 통과한 인스턴스만 다음 표본에 사용합니다. 화면이 잠깐 어긋났다가 돌아오면 복구까지 걸린 시간과 오류 내용을 모두 남깁니다. 처음부터 맞았던 표본과 복구된 표본은 따로 집계합니다.
그래프의 안정화 시간은 올바른 화면에 도달한 표본 전체를 대상으로 계산합니다. 복구 시간은 중간 화면 오류를 거쳐 정상 화면으로 돌아온 표본만 따로 모은 값입니다. 현재 게재 결과는 2초 기준을 초과한 조건을 막대 대신 복구 실패로 표시합니다.
실패 판정
다음 현상을 프레임마다 검사합니다.
- 행 ID의 누락·중복·잘못된 순서
- 예상 높이와 다른 행
- 행 사이의 빈틈과 겹침
- 화면 위아래에 생긴 빈 영역
- 같은 높이 조건의 전체 스크롤 높이 오차
- 목록 변경 범위를 벗어난 기준 행의 위치 변화
한 프레임이라도 어긋나면 화면 오류로 기록합니다. 잘못된 배치가 300ms 이상, 8프레임 동안 같은 상태로 머물면 복구 실패로 판정합니다. 현재 게재 결과에서는 좌표가 계속 바뀌는 동안 최대 2초까지 검사를 이어갑니다. Sectile에서 발생한 오류는 원본 결과에서 fatal로 구분합니다.
초기 렌더와 스크롤
두 행 구성 모두 720 × 480px 화면에 10만 개 행을 넣습니다. 라이브러리마다 8행 분량의 여유 구간을 요청했습니다. 같은 높이 실험은 정확한 72px을 사용하고, 서로 다른 높이 실험의 공통 예상값도 72px로 맞춥니다.
각 조건의 첫 인스턴스는 새로운 동일 출처 브라우징 컨텍스트에서 한 번 실행합니다. 이 진단값은 처음 올바른 배치와 다음 브라우저 표시 기회를 따로 기록하고 웜 점수에는 합치지 않습니다. 본 러너는 측정하지 않는 준비 마운트를 한 번 더 수행한 뒤 라이브러리 순서를 바꿔 가며 세 번에서 다섯 번 측정합니다. 웜 렌더는 스크롤 영역이 DOM에 반영된 시점, 첫 행이 나타난 시점, 처음 보이는 화면의 배치가 맞는 시점으로 나눠 기록합니다. 세 번째 실행부터 누적 중앙값과 p95가 안정되면 종료하고, 분포가 계속 바뀌면 다섯 번째 실행까지 이어갑니다. 같은 높이 조건에서는 전체 스크롤 높이까지 맞아야 완료됩니다. 서로 다른 높이 조건의 전체 높이 오차는 별도 지표로 남깁니다.
매 실행에서 처음 다섯 번은 준비 과정으로 빼고, 이어지는 스크롤 20번을 기록합니다. 러너는 프레임 경계가 지난 뒤 scrollTop을 바꿉니다. 브라우저가 스크롤 이벤트를 전달하기 시작한 순간부터 바뀐 DOM의 좌표를 모두 읽은 순간까지를 스크롤 시간으로 잽니다. 이 좌표로 행 순서와 높이, 겹침, 화면의 빈틈을 확인합니다. 같은 높이 조건에서는 목표 행과 전체 스크롤 높이도 함께 검사합니다. 판정 코드의 실행 시간은 별도 probe로 기록합니다.
그래프에는 좌표 읽기 비용까지 포함한 상한을 씁니다. 원본 결과에는 좌표를 읽기 전의 하한, 좌표 읽기 비용, 검사 횟수, 라운드와 표본 번호를 기록했습니다. 중앙값 절대 편차와 라운드별 범위도 함께 남겨 느린 라운드의 분포를 드러냅니다.
목록부터 자유 좌표 배치까지의 결과는 2026년 8월 31일, Chrome 151, Apple Silicon, macOS에서 커밋 ce84a7b의 clean production build로 다시 측정했습니다. Sectile은 모든 지원 초기 렌더와 변경 조건을 오류 없이 완료했습니다. 절대 시간은 기기와 브라우저 상태에 따라 달라집니다. 라이브러리 사이의 상대 차이와 정확도 결과를 함께 읽어야 합니다.
비교 대상은 TanStack Virtual, react-window, React Virtuoso, react-virtualized, Virtua, Vue Virtual Scroller입니다. 실행 코드와 원본 JSON은 benchmarks/virtual-ecosystem에 있습니다.
pnpm --filter @sectile/benchmark-virtual-ecosystem devSectile 배치 계산
브라우저 비교에는 프레임워크와 DOM 작업이 모두 들어갑니다. 아래 명령은 그 비용을 빼고 @sectile/virtual의 크기 색인과 화면 범위 조회, 배치 결과 생성만 측정합니다.
pnpm --filter @sectile/virtual benchmark2026년 8월 27일, Node 24.19.0, Apple Silicon, macOS에서 얻은 중앙값입니다.
| 작업 | 중앙값 |
|---|---|
| 크기 위치 왕복 조회, 10만 항목 | 0.676 µs |
| 크기 위치 왕복 조회, 100만 항목 | 0.870 µs |
| 선형 목록의 화면 범위 계산, 10만 항목 | 1.237 µs |
| 선형 목록의 배치 결과 생성, 10만 항목 | 7.694 µs |
| 선형 목록의 바뀐 크기 32건 반영, 10만 항목 | 2.857 µs |
| 희소 격자 화면 조회, 10만 영역 | 13.841 µs |
| 희소 격자의 바뀐 행 높이 32건 반영 | 5.451 µs |
| 벽돌형 화면 조회, 10만 항목·8열 | 21.873 µs |
| 자유 좌표 화면 조회, 10만 항목 | 39.982 µs |
| 배치 방식 | 10만 항목 검색 구조 구성 |
|---|---|
| 희소 격자 | 45.742 ms |
| 벽돌형, 8열 | 16.467 ms |
| 자유 좌표 검색 트리 | 73.927 ms |
화면 영역은 항목 전체를 가로질러 계속 이동하며 매번 다른 좌표를 조회합니다. 내부 원본 보고서에는 partitioned track-grid 구성, 고정 영역 조회, 바뀐 트랙 배치도 포함합니다. 성능 변화를 확인할 때는 같은 컴퓨터에서 변경 전후를 다시 측정해야 합니다.
