벤치마크 #2: 지속 쓰기 성능 테스트 (성능 유지력)

노바칩스의 제품 소개 페이지에서 특히 강조하고 있는 게 있는데, 연속 쓰기를 계속해도 성능이 떨어지지 않는다고 강조한다. 참고로 최대 쓰기 속도가 GB/s대인 NVMe SSD는 당연하고, 최대 1000MB/s정도에 불과한 USB SSD도 저렇게 성능 유지가 잘 되는 제품은 드물다. 삼성 T7 Shield가 그나마 괜찮은 편인데 그마저도 성능이 약간은 떨어진다. 의심이 많은 나로서 이런 걸 그냥 지나칠 수 없다. 당연히 테스트해 보지 않을 수 없었다.
특히 카메라에서 연사로 기록하면 수 GB에서 수십 GB에 이르는 데이터를 연속으로 기록하는 일이 자주 생기는 만큼, 일반적인 NVMe SSD처럼 캐시가 찬 뒤 금방 속도가 떨어진다면 큰 문제가 된다.
그런데 sustained write speed를 측정할 수 있는 널리 사용되는 툴이 나래온 더티 테스트 외에는 딱히 없는 것 같아서, 하나 만들었다. 랜덤 데이터를 원하는 Chunk 단위로 지속적으로 기록하면서 성능을 측정할 수 있는 간단한 유틸리티를 Swift로 만들었다. 코드는 아래처럼arc4random_buf
로 랜덤 데이터를 생성하고, 운영 체제 수준에서의 버퍼가 동작하지 않도록 O_SYNC
와 F_NOCACHE
상태로 드라이브에 데이터를 청크 단위로 기록하면서 속도를 측정하는 간단한 구조이다.
for chunkNumber in 0..<numberOfChunks { if signalReceived { print("\nStopping test due to interrupt signal...") break } if config.verify { randomGenerator.fillBuffer(withSeed: UInt32(chunkNumber)) } else { randomGenerator.fillBuffer() } let chunkData = randomGenerator.getData() if config.verify { originalChunks.append(chunkData) } if !writeChunk(fd: fd, data: chunkData) { print("Failed to write chunk \(chunkNumber)") break } let chunkEndTime = CFAbsoluteTimeGetCurrent() totalBytesWritten += UInt64(config.chunkSize) if totalBytesWritten - lastRecordedBytes >= UInt64(config.recordIntervalBytes) { let intervalTime = (chunkEndTime - lastRecordedTime) * 1000 let intervalBytes = totalBytesWritten - lastRecordedBytes let intervalSpeedMBps = (Double(intervalBytes) / (1024 * 1024)) / (chunkEndTime - lastRecordedTime) csvLines.append("\(intervalNumber),write,\(totalBytesWritten),\(String(format: "%.3f", intervalTime)),\(String(format: "%.2f", intervalSpeedMBps))") let progress = Double(totalBytesWritten) / Double(totalBytesToWrite) * 100 print("Progress: \(String(format: "%.1f", progress))% - Interval \(intervalNumber) - Write Speed: \(String(format: "%.2f", intervalSpeedMBps)) MB/s") lastRecordedBytes = totalBytesWritten lastRecordedTime = chunkEndTime intervalNumber += 1 } }
Python이나 Node로 만들었다면 더 편했겠지만, 스크립팅 언어는 성능이 떨어질 수 있으므로 Swift, Rust, Go 중 고민하다가 macOS에서 간단히 돌리기 위해 Swift를 사용했다. 측정 전 메모리카드는 TRIM을 실행시켰고, GC가 충분히 돌 수 있도록 전원을 연결한 채 2시간 이상 대기했다.
그리고 돌렸는데…

72GB 지점 부근에서 갑자기 프리징같은 현상이 생기고 파일 시스템 sync에 실패함. sync에 실패한 이후 커널패닉이 나서 제대로 측정하지 못했다. 이건 연구를 좀 해 봐야 할 듯하다. 가지고 있는 M1 MacBook과 M4 MacBook 모두 같은 증상이 발생했다. macOS – exFAT의 문제일 수도 있을 듯 한데, 그냥 윈도우에서 해 봐야 할 듯 하다. 일단 윈도우용 툴을 Rust로 다시 짜긴 했으나, 퍼포먼스가 잘 안 나오는 문제가 있어서 나래온 더티 테스트를 사용했다.
나래온 더티 테스트 (성능 유지 능력)
그래서 잘 알려진 SSD 성능 유지 능력 테스트 툴인 나래온 더티 테스트를 사용해서 테스트했다. 메모리카드의 사실상 전체 공간(10MB를 남긴 전체)을 가득 채우도록 설정했다. 나래온 더티 테스트는 Mersenne Twister 기반의 랜덤 값을 디스크에 기록하는 테스트로, 랜덤 값으로 디스크 전체를 채우면서 중간에 성능이 떨어지는지 실험한다. 거의 모든 SSD는 비교적 빠른 쓰기가 가능한 버퍼 영역(pSLC)이 있고, 이후 메인 영역에 기록이 이루어지기 시작하면 성능이 급격히 떨어진다. 특히 PCIe 4.0 NVMe SSD는 거의 모든 제품이 TLC인데, TLC는 절대로 스펙상 최대 쓰기 속도인 5000-6000MB/s의 쓰기 속도를 낼 수 없기 때문에 무조건적으로 버퍼를 활용한다. 때문에 메모리카드가 거의 가득 찬 상태가 되면 버퍼에 여유 공간이 없어지고, 속다가 급격히 느려진다.

테스트 결과 정말로 노바칩스에서 공언한 대로 성능 저하가 아예 없었다. 메모리카드가 100% 채워지는 동안 1353MB/s의 경이로운 평균속도를 유지했다.

800GB (윈도우에서 인식되는 용량은 745GiB)의 전체 용량에 대해 쓰기 테스트를 실행했는데 전체 테스트 구간에 걸쳐 쓰기 속도는 1.3GB/s대로 일정하게 유지되었다. 현존하는 거의 모든 데스크탑용 M.2 SSD는 물론이고, 노바칩스 CFexpress 카드보다 느린 SATA SSD와 USB SSD조차도 버퍼를 다 채우고 나면 급격한 성능 저하가 일어나는데, 노바칩스 CFexpress 4.0 Type A 카드는 하위 라인업인 Express 시리즈에서조차 성능 저하를 전혀 확인할 수 없었다는 건 놀라웠다.
한 번의 쓰기 테스트는 메모리 컨트롤러를 잘 만들고, over-provisioning(여유 공간)을 충분히 잡으면 성능 저하 없이 통과할 수도 있다. 하지만 이러한 경우에도 수 TB에 이르는 쓰기 작업을 계속하면 메모리의 캐시가 다 떨어지고 TLC의 실제 쓰기 속도까지 성능이 떨어진다. 그래서 무한 반복 모드를 돌려봤다.

5회차를 돌아도 성능 저하가 없다.

그렇게 총 한 시간이 넘는 시간에 걸쳐 6회 연속, 용량으로는 4470GiB에 이르는 용량의 쓰기를 지속했는데 여전히 성능 저하는 전혀 관찰할 수 없었다. 아래는 6번째 테스트에서의 쓰기 성능 그래프이다. 4470GiB의 용량이면 소니의 A1M2의 최대 연사 속도에서도 프로세싱 속도 제한 때문에 한시간만에 기록할 수 없는 양이다. 이 정도 용량을 기록하는 것은 일반적인 사용자의 사용 범위를 아득히 뛰어넘는 사용 방식이라고 생각한다. 따라서 일반 사용자는 이 메모리카드의 성능 저하를 전혀 체감할 수 없을 것이다.

Discover more from ATIK.KR
Subscribe to get the latest posts sent to your email.