- Sep 0.10.0은 AMD 9950X(Zen 5) 같은 AVX-512 지원 CPU 최적화로 저수준 CSV 파싱에서 21 GB/s를 달성해, 이전 약 18 GB/s보다 빨라짐
- 성능 향상은.NET 9.0의 AVX-512 코드 생성에서 마스크 레지스터 왕복이 생기는 병목을 줄이도록 파서 구조를 바꾼 데서 나옴
- 새 AVX-512-to-256 파서는 512비트로
char를 로드한 뒤 256비트 바이트 벡터로 변환해, 마스크 처리와 별도 순열 비용을 피함 - Sep의 저수준 파싱 성능은 2023년 0.1.0의 5950X/.NET 7.0 기준 약 7 GB/s에서 0.10.0의 9950X/.NET 9.0 기준 약 21 GB/s로 약 3배 개선됨
- 상위 벤치마크에서도 9950X 멀티스레드 Sep은 package assets 100만 행을 72.213ms, 약 8.0 GB/s로 처리했고 floats 데이터도 약 8.1 GB/s를 기록함
Sep 0.10.0의 목표와 결과
- Sep 0.10.0은 2025년 4월 22일 릴리스됐고, AMD 9950X(Zen 5) 같은 AVX-512 지원 CPU 최적화와 9950X 벤치마크를 포함함
- 저수준 CSV 파싱 기준 Sep은 9950X에서 21 GB/s를 달성함
- 0.10.0 이전에는 같은 9950X에서 약 18 GB/s였음
- 분석 범위는 package assets CSV 데이터의 저수준
Rows파싱이며, 모든 수치는 단일 스레드 기준임 - 벤치마크 수치는 몇 퍼센트포인트 변동될 수 있어, 특정 릴리스에서 작은 회귀가 보일 수 있음
0.1.0부터 0.10.0까지의 성능 변화
- Sep 성능은 코드 변경, .NET 버전 변화, CPU 세대 변화가 함께 작용하며 점진적으로 개선됨
- 대표 성능 흐름은 다음과 같음
- 0.1.0, 5950X, .NET 7.0: 약 7 GB/s
- 0.3.0, 5950X, .NET 8.0: 약 12 GB/s
- 0.6.0, 5950X, .NET 9.0: 약 13 GB/s
- 0.9.0, 9950X, .NET 9.0: 약 18 GB/s
- 0.10.0, 9950X, .NET 9.0: 약 21 GB/s
- 2023년 6월 Sep 공개 이후 2년이 조금 안 되는 기간에 약 3배 빨라짐
- 5950X에서 Sep 0.9.0을 쓴 경우와 9950X에서 Sep 0.10.0을 쓴 경우를 비교하면 약 1.6배 개선됨
- 9950X의 부스트 클럭은 5.7GHz, 5950X는 4.9GHz임
- 이 클럭 차이만으로 약 1.2배를 설명할 수 있다고 봄
.NET AVX-512 코드 생성의 마스크 레지스터 병목
- Sep은 0.2.3부터 AVX-512를 지원했지만, 당시 .NET 8은 AVX-512의
k1-k8마스크 레지스터를 명시적으로 지원하지 않았음 - 기존 AVX-512 코드 생성에서는 비교 결과가 마스크 레지스터에 들어간 뒤 일반 레지스터로 옮겨지고, 다시 마스크 레지스터로 돌아가는 흐름이 생김
- 9950X로 업그레이드한 뒤 Sep 0.9.0은 저수준 CSV 파싱에서 약 18 GB/s를 기록했고, 이는 5950X보다 약 1.4배 빠른 결과였음
- 환경 변수로 파서를 바꿔 비교하자, 9950X에서 AVX2 파서가 약 20 GB/s를 기록해 기존 AVX-512 파서보다 약 10% 빨랐음
- 이 차이로 AVX-512 마스크 레지스터 처리가 여전히 성능에 영향을 주고 있음이 확인됨
Sep 파싱 루프의 기본 구조
- Sep의 모든 파서는 같은 기본 구조를 따르며,
Parse제네릭 메서드 하나로 따옴표 처리 여부가 다른 두 경로를 지원함ParseColInfos: 따옴표 처리 시 사용하며 더 많은 상태 추적이 필요함ParseColEnds: 따옴표 처리가 없을 때 사용함
- 파싱은 배열에서 가져온
charspan 단위로 수행되며, 예시는 16K 크기임- 이 크기는 CPU 캐시에 들어갈 만큼 작고, 이후 효율적인 멀티스레딩에도 유리함
- 루프는 SIMD 레지스터에 16비트 문자 데이터를 로드하고, 이를 바이트 SIMD 레지스터로 변환한 뒤 CSV 특수 문자를 비교함
- 비교 대상에는
\n,\r,",;등이 포함됨
- 비교 대상에는
- 비교 결과는 비트마스크로 변환되고, 마스크에서 설정된 비트만 순차적으로 파싱됨
- 성능 차이는 이 SIMD C# 코드가 .NET에서 어떤 머신 코드로 JIT 컴파일되는지에 크게 좌우됨
기존 AVX-512 파서와 0.10.0의 조정
- 0.9.0의
SepParserAvx512PackCmpOrMoveMaskTzcnt는 두 개의 512비트 SIMD 레지스터에 각각 32개char를 로드하고, 이를 하나의 512비트 바이트 벡터로 패킹해 루프당 64개 문자를 처리함 - 패킹된 데이터는 순서가 섞여 있어
PermuteVar8x64로 다시 정렬해야 함 - .NET 9.0의 어셈블리에서는 각
Vec.Equals가vpcmpeqb와vpmovm2b두 명령으로 이어졌고,k1같은 마스크 레지스터와zmm일반 벡터 레지스터 사이 이동이 반복됨 - Sep 0.10.0에서는
MoveMask호출을 더 앞당겨, 마스크 레지스터와 일반 레지스터 사이 왕복 횟수를 줄임- 다른 파서에서는 “특수 문자가 없는” 빠른 경로에서 명령 수를 줄이기 위해 필요할 때만
MoveMask를 호출함
- 다른 파서에서는 “특수 문자가 없는” 빠른 경로에서 명령 수를 줄이기 위해 필요할 때만
- 조정 후에도 마스크 레지스터에서 일반 레지스터로 이동은 남지만, 전체 어셈블리 명령 수는 감소함
AVX2와 새 AVX-512-to-256 파서
- AVX2 기반
SepParserAvx2PackCmpOrMoveMaskTzcnt의 어셈블리는 마스크 레지스터가 없어 더 직선적인 구조를 가짐 - 이 구조 덕분에 AVX2 파서는 기존 0.9.0 AVX-512 파서보다 빨랐음
- 0.10.0의 새
SepParserAvx512To256CmpOrMoveMaskTzcnt는 AVX-512 명령으로char를 로드한 뒤ConvertToVector256ByteWithSaturation으로 256비트 바이트 벡터를 만듦- 실제 명령은
vpmovuswb임 - 루프당 처리량은 “단지” 32개
char지만 구조가 더 단순함
- 실제 명령은
- 이 방식은 512비트 마스크 레지스터 문제를 피하고, 패킹된 데이터가 이미 올바른 순서로
ymm4에 있어 별도 순열도 필요 없음 - 새 파서가 9950X에서 Sep 파싱 성능을 약 21 GB/s까지 끌어올림
파서별 9950X 저수준 벤치마크
- AMD 9950X에서 환경 변수로 모든 파서를 실행해 비교한 결과, 새 AVX-512-to-256 파서가 가장 빨랐음
- 주요 결과는 다음과 같음
SepParserAvx512To256CmpOrMoveMaskTzcnt: 21597.7 MB/s, 27.0 ns/row, 1.351msSepParserVector256NrwCmpExtMsbTzcnt: 20608.5 MB/s, 28.3 ns/row, 1.416msSepParserAvx2PackCmpOrMoveMaskTzcnt: 20599.3 MB/s, 28.3 ns/row, 1.417msSepParserAvx512PackCmpOrMoveMaskTzcnt: 19944.3 MB/s, 29.3 ns/row, 1.463ms
Vector256기반 크로스플랫폼 파서는 AVX2와 거의 같은 수준에 도달함Vector128및Vector512기반 크로스플랫폼 파서는 여전히 빠르지만 5~10% 느렸고,Vector512는Vector128보다 느렸음SepParserIndexOfAny는 2787.0 MB/s로 크게 뒤처졌고,Vector64는 9950X에서 가속되지 않아 459.9 MB/s에 머묾
5950X와 9950X의 상위 벤치마크
- package assets 데이터에서 100만 행 처리 결과는 9950X가 5950X보다 크게 빨랐음
- 5950X
Sep_MT: 119.430ms, 4888.1 MB/s - 9950X
Sep_MT: 72.213ms, 8084.1 MB/s
- 5950X
- 9950X에서 단일 스레드
Sep은 package assets 100만 행을 291.979ms, 1999.4 MB/s로 처리함 - 같은 9950X package assets 벤치마크에서 비교 대상은 다음 성능을 보임
Sylvan: 413.265ms, 1412.6 MB/sReadLine_: 377.033ms, 1548.4 MB/s, 할당량 1991.04MBCsvHelper: 1005.323ms, 580.7 MB/s
- floats 데이터에서도 9950X의 멀티스레드 Sep은 25,000행을 2.497ms, 8136.8 MB/s로 처리함
- 5950X에서 9950X로의 상위 벤치마크 개선 폭은 저수준 벤치마크와 비슷하게 약 1.5~1.6배임