iOS 최적화된 디스크 쓰기 관리
데이터 저장을 위해 디스크에 쓰는 행위는 배터리 수명 및 저장 장치의 수명에 영향을 미침.
디스크 쓰기를 줄이는 방법을 이해하면 앱 성능을 최적화하고 사용자 경험을 개선할 수 있음.
일반적으로 모든 iOS기기와 일부 macOS는 영구 저장소로 SSD를 사용함. SSD나 저장 매체에 있는 데이터에 엑세스 하는 속도는 RAM에 비해서 느림. Xcode와 Instruments를 사용하여 앱의 디스크 쓰기 성능을 파악할 수 있음.
SSD 접근 최적화하기
시스템이 SSD의 블록에 쓰기 작업을 수행할 때 해당 블록에 대한 새로운 읽기 요청은 기존 수행하는 쓰기 작업이 완료할 때 까지 대기함.
- SSD에 쓰는 것은 읽기 보다 느린 작업.
- 읽기 및 쓰기 요청을 인터리빙하면 앱의 성능이 느려질 수 있음
- 디스크 인터리빙이란?
- 저장 장치에서 읽기와 쓰기 작업을 교차로 수행.
- 헤드 이동의 증가로 인하여 성능 저하의 단점도 존재.
- 인터리빙에 대한 자세한 설명은 컴퓨터 구조 이론 참고
- 디스크 인터리빙이란?
앱에서 과도한 쓰기 작업을 방지함.
MetricKit을 사용하면 Firebase 등을 사용하지 않고도 퍼스트파티를 통해 분석할 수 있음.
- Window > Organization을 통해 정보 확인 가능
중요한 디스크 쓰기를 유발하는 코드 식별
Instruments의 FileSystem Activity를 통해서 확인 가능
- 분석하는데 시간이 좀 오래걸림
파일 시스템 데이터를 메모리에 매핑하는 시스템 호출의 형태로 논리적 파일 시스템을 기록.
- 디스크 사용 및 디스크 I/O 대기 시간은 읽기 및 쓰기의 크기와 대기시간을 포함하여 파일 시스템 이벤트로 발생하는 저장 매체의 물리적 사용을 리포트함.
- NOTE
- 디스크의 물리적 읽기 및 쓰기 크기는 논리적 파일 시스템 활동의 크기와 상관 없음
- 디스크 컨트롤러는 블록(4KB) 이라는 영역에서 작동
- 디스크에 대한 쓰기는 블록 단위로 발생하여 단일 바이트만 변경된 파일을 저장해도 디스크에 4KB가 기록됨.
여러 쓰기 작업을 일괄 처리하기.
같은 파일을 반복적으로 열고, 저장하고, 닫는 작업을 수행하면 디스크 쓰기 빈도라 높아짐.
- 이를 매번 단일로 처리할 경우 메모리 사용량이 증가할 수 있음
- 적절하게 묶어서 처리할 수 있도록 설계하기
직렬화된 파일로 디스크 저장 최소화
많은 앱이 문서를 저장하기 위해서 데이터를 저장할 때 Property List, JSON, XML과 같은 데이터를 직렬화된 형식을 사용.
- 이런 형태는 번들 메타데이터 같이 읽기 전용 혹은 네트워크를 통해 데이터를 전송할 때 유용
- 자주 변경되는 문서를 저장하는데는 적합하지 않음.
- 직렬화된 문서를 변경할 경우 전체 파일을 다시 써야 해서 작업 지연 시간이 증가하고, 저장 장치의 마모가 심해질 수 있음.
가능하다면, 자주 편집되는 문서의 경우에는 CoreData(SwiftData) 또는 SQLite 사용할 것
- 만약 사용이 어려울 경우 자주 변경되는 데이터와 정적인 데이터를 서로 다른 직렬화된 파일로 분리.
- 이렇게 할 경우 디스크 쓰기 횟수를 줄이고 지연 시간 개선 가능.
빠른 파일 생성 및 삭제 방지
iOS에서 파일 생성 및 삭제에는 8KB의 메타데이터를 작성하여 디렉토리 참조를 업데이트 함.
- iOS에서 많은 쓰기가 발생할 경우 성능이 저하되고 장치의 마모 증가
- iOS에서 파일 이름을 바꾸거나 이동하면 파일 시스템 메타데이터 쓰기에 최대 16KB가 추가
파일을 원자(atomic) 단위로 작성하는 작업은 디스크 쓰기 작업을 발생시킴. NSString, NSArray, NSDictionaray, NSData 등에서 atomic 옵션을 사용하여 파일에 데이터를 저장할 때 해당 방식 사용
- Atomic Write란?
- 파일을 쓰는 도중에 중간 단계에서 데이터가 손상되거나 잘못된 파일이 남는 것을 방지하는 방식
- 안정성을 위해 추가적 작업 수행
- Atomic Write 동작 방식
- 임시 파일 생성하여 데이터를 임시 파일에 작성
- 원래 파일 제거 (unlink)
- 임시 파일 이름 변경
- 임시 파일의 생성 및 삭제 그리고 파일 이름 변경으로 인한 디스크 쓰기 작업 추가
let string = "welocome swift6"
let path = "/carrot/devfile/file.txt"
// 아토믹하게 저장
try! string.write(
toFile: path,
atomically: true,
encoding: .utf8
)
명시적으로 저장소 동기화하는 작업을 최소화하기
iOS에 데이터 쓰면 통합 버퍼 캐시에 추가되며, 시스템은 이를 파일 저장소에 작성함.
- 가급적이면 명시적 동기화 하지 말것
fsync(_: )
fcntl(_:_:)
F_FULLSYNC
디스크 성능 측정을 통해 최적화 상태 점검
XCTest를 활용해 성능 측정할 수 있음.
func testDiskUse() {
self.measure(metrics: [XCTStorageMetric()]) {
// This is a disk-intensive operation.
}
}
자주 변경되는 사항은 SQLite 혹은 CoreData 사용하기
SQLite는 저장소에 대한 효율적인 접근을 보장하기 위해 고도로 최적화되어 있음.
- 메모리 내 캐시 및 디스크 일괄 처리 등 사용성 높은 성능과 저장소의 최소한의 마모 상태를 보장
- 데이터 구조는 새 콘텐츠 삽입 및 기존 콘텐츠 업데이트할 때 효율적으로 업데이트하게 설계되어 있음
CoreData는 SQLite의 효율적인 디스크 사용을 활용해 데이터를 저장
- SQLite를 열고 닫는 작업은 비용이 비싼 일관성 검사 및 저널링 등으로 인하여 메타데이터를 작성해야하는 값비싼 작업으로 불필요하게 SQLite를 닫지 말것
- Transaction은 원자적으로 동작하여 데이터베이스가 일관된 상태로 남게됨.
- 적절한 index를 사용하여 검색 시간을 줄이고 불필요한 디스크 쓰기 작업을 방지할 것
- column에 index가 없으면 SQLite는 임시적으로 B-tree를 구성해 전체 테이블을 읽고, B-tree를 활용해 정렬을 수행함.
(참고)
'apple > iOS, UIKit, Documentation' 카테고리의 다른 글
iOS 히치(hitch)에 대해서 알아보기 (2) | 2024.12.26 |
---|---|
Swift URLProtocol (URLSessionConfiguration) (0) | 2024.12.23 |
Swift Mixin and Trait (1) | 2024.11.18 |
[Swift] Timer + RunLoop, backgroundQueue (swift-corelibs-foundation) (3) | 2024.10.15 |
iOS CoreData Relationships (0) | 2024.10.13 |