일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 애플 디벨로퍼 아카데미 21주차 회고
- Swift 문법
- 애플 디벨로퍼 아카데미
- StateObject
- 애플 아카데미 후기
- 숭실대
- 운영체제
- 데이터베이스
- OS
- global soop
- 소프트웨어분석및설계
- 네이버 부스트캠프
- swift문법
- react
- SWIFT
- apple developer academy 후기
- Swift 디자인패턴
- useReducer
- iOS 개발 오류
- 치지직
- 네이버 치지직
- 제앱소
- ObservableObject
- Apple Developer Academy @ POSTECH
- ObservedObject
- Swift 기능
- sqoop
- 애플 디벨로퍼 아카데미 후기
- 데이터베이스 공부
- 앱 비교 프로젝트
- Today
- Total
사과하는 제라스
[Swift 지식] removeAll(keepingCapacity: Bool)의 성능 비교 본문
[Swift 지식] removeAll(keepingCapacity: Bool)의 성능 비교
Xerath(제라스) 2024. 3. 14. 22:04목차
서론
안녕하세요~ 개발자 제라스입니다👋🏻🤖👋🏻
제가 요즘 HackingWithSwift로 SwiftUI를 학습하면서 아주 기초적인 것부터 학습 중인데요.
Array 타입에서 removeAll을 쓰는 과정에서 removeAll(keepingCapacity: Bool) 이란 옵션이 있더라구요!
평소같으면 그런가보다하고 넘어갔겠지만..!
또 갑자기 쓸 데 없는 집착이 생겨버렸습니다 ㅠㅠㅠ
그래서 이번 기회에 심심하게나마 한번 그 효과랑 성능에 대해 얘기해보고자 합니다.(생각보다 큰 효과는 없었지만...그래도 읽어주소 😭😭)
그럼 시작해보겠습니다.
Array의 removeAll 옵션 - keepingCapacity
저는 removeAll에 이런 옵션이 있는지도 몰랐습니다ㄷㄷㄷ
의역하면 '용량을 유지하면서 싹 다 삭제?'정도가 되겠네요.
이 옵션은 default값이 false라서 그냥 다음과 같이
var a = Array(1...1000)
a.removeAll() // == a.removeAll(keepingCapacity: true)
print(a.count) // 0
print(a.capacity) // 0
이렇게 하면 그저 배열도 사라지고 메모리에서 사라지기도 하죠.
하.지.만
var a = Array(1...1000)
a.removeAll(keepingCapacity: false)
print(a.count) // 0
print(a.capacity) // 1000
이렇게 쓰면 용량은 유지, 즉, 메모리에서 해제되지 않은 채 그 용량 그대로 남는 겁니다.
그래서 removeAll 이후 count는 0이, a.capacity는 1000이 나오는 겁니다.
그래서 '그럼 이렇게 유지하면 뭐가 좋나?' 생각이 들어 찾아봤는데,
어떤 사람은 반드시 유지하면서 쓰는게 좋다는 둥의 얘기를 했다고 하더라구요?
HackingWithSwift에서 나눈 질의응답을 본 결과, Apple이 default로 false로 둔 이유는 메모리적인 측면에서 굳이 삭제 후 들고 있을 필요가 없고, 그게 더 일반적으로는 안전하기 때문이라고 합니다.
하지만, 만약 배열을 비우고 다시 새 값들을 채워넣을 때는 오히려 이렇게 capacity가 변하지 않는 것이 더 성능이 좋다고 합니다.
간단히 생각해봐도 새로 계속 값을 추가해서 용량의 변화를 내는 것보다는 용량은 유지한 채 넣는게 메모리 작업이 덜 들겠죠?ㅎㅎ
keepingCapacity 값의 차이에 따른 refill 속도
일단 배열의 길이가 짧을 때는 keepingCapacity 여부에 상관없이 속도는 둘 중 랜덤으로 빠르다고 나오더라구요.
언제는 false인게 빠르고 언제는 true인게 빠르고 했습니다.
그래서 HackingWithSwift의 Paul 아저씨의 조언에 따르면 큰 용량(10 million이라고 했지만 전 50000정도만 했습니다 ㅎㅎ)일 때 그 효과가 나타난다길래...
이렇게나 메모리 차지를 하고 스왑핑까지 해대면서 실행해본 결과
keeping capacity가 더 빠르게 채워지는 효과를 보았습니다.
5차례 수행해도 똑같은 결과가 나왔는데 이정도면 효과가 있지 않나 싶네요ㅎㅎ
코드는 다음과 같습니다.
import UIKit
func keeping() -> Double {
var arr = Array(1...50000)
let startTime = CFAbsoluteTimeGetCurrent()
arr.removeAll(keepingCapacity: true)
for i in 0..<50000 {
arr.append(i)
}
let duration = CFAbsoluteTimeGetCurrent() - startTime
return duration
}
func nonKeeping() -> Double {
var arr = Array(1...50000)
let startTime = CFAbsoluteTimeGetCurrent()
arr.removeAll(keepingCapacity: false)
for i in 0..<50000 {
arr.append(i)
}
let duration = CFAbsoluteTimeGetCurrent() - startTime
return duration
}
let keepingDuration = keeping()
let nonKeepingDuration = nonKeeping()
print("keepingCapacity: true일 때:", keepingDuration)
print("keepingCapacity: false일 때:", nonKeepingDuration)
if keepingDuration < nonKeepingDuration {
print("Keeping capacity가 빠릅니다.")
} else if keepingDuration > nonKeepingDuration {
print("Not keeping capacity가 빠릅니다.")
} else {
print("둘 속도가 같습니다.")
}
removeAll()의 시간복잡도가 O(n)???
사실 생각해보면
var a = Array(1...100)
a = []
이렇게 해버리면 시간복잡도가 O(1)입니다.
상당히 빠르죠.
반면, removeAll()해버리면 배열을 순회하면서 하나씩 다 지웁니다.(이건 keepingCapacity와 상관없이요!)
공식문서에도 O(n)이라고 나와있죠.
그럼 removeAll을 쓸 필요가 없네!!?? 굳이??
맞습니다. 비울 때는 그럴 필요가 없지만 removeAll은 대신 여러가지 옵션들이 있죠. keepingCapacity, where 등등...
그래서 상황에 따라 오히려 removeAll이 필요한 경우가 있습니다.
결론, 둘 다 필요하다. 하지만 평상 시에 배열을 비우는 경우엔 []가 시간복잡도 면에선 훨씬 나이스하긴 하다.
마무리
오늘은 조금 가벼운 주제로 진행해보았는데 이렇게 성능 테스트하면서 막연한 생각이 실제로 보여지니 재밌더라구요 ㅋㅋㅋ
원래 뭐 배우는 것보다 써보는게 재미지죠😅😅
껄껄...
아무튼 이렇게 용량을 유지하는 법도 있지만 반면 이걸 쓰는게 무조건적으로 좋은 것은 아닙니다.
메모리 차지를 그대로 하기 때문이죠...
그래서 정말 배열을 비우고 다시 그만큼 즉시 채우는 것이 아니라면 남용하는 것이 좋은 것만은 아닌 듯합니다ㅎㅎ
오늘도 포스팅 끝까지 읽어주셔서 감사합니다!
다음에도 좋은 포스팅으로 돌아오겠습니다 :-D
추가 학습 내용 - capacity는 2배씩 늘어난다
이건 포스팅을 한지 5일이 지난 지금 추가해서 학습하게 된 내용을 작성해봅니다!
아니... 오늘 SwiftUI 공부를 좀 더 하다보니까 길이가 3짜리인 배열을 removeAll(keepingCapacity: true)를 하고 나니까
남은 capacity가 4더라구요!!!!!!!
엥!?!?!?!?!
근데 몇번을 해도 4인 걸 보고는 왜 그러지???하고 찾아보니 공식문서에서도
'The new storage is a multiple of the old storage’s size.'
즉, 배열에 요소를 추가하게 되는데 만약 가득 차면 그 다음 용량은 원래 것의 2배를 채우는 방식입니다.
예를 들어 어떤 배열에 총 5개의 객체를 추가하는 코드를 봅시다.
class User {
let id: Int
init(id: Int) {
self.id = id
}
deinit {
print("Bye Bye")
}
}
var users = [User]()
for i in 1...5 {
let user = User(id: i)
users.append(user)
}
print(users.count)
users.removeAll(keepingCapacity: true)
print(users.capacity)
이렇게 5개의 User 객체가 users 배열에 append 된다고 합시다.
일단, 결과부터 보면 다음과 같습니다.
5
Bye Bye
Bye Bye
Bye Bye
Bye Bye
Bye Bye
8
왜 count는 5였는데 capacity가 8일까요?
간편하게 user1부터 user5까지 넣는 과정이라고 합시다.
user1이 들어가면 capacity는 0에서 1이 됩니다.
근데 이제 꽉찼죠?
그래서 user2가 들어갈 때 capacity는 1의 2배인 2가 됩니다.
근데 이제 또 꽉찼죠?
그래서 user3이 들어갈 때 capacity는 2의 2배인 4가 됩니다.
그 다음 user4를 넣습니다.
근데 또 꽉찼죠?
그래서 마지막으로 user5가 들어갈 때 capacity는 4의 2배인 8이 됩니다.
정말 간단하죠? 넣으려는데 꽉차 있으면 capacity를 2배로 늘리는 거에요!!
이건 몰랐는데...!! 실제로 테스트해보고서 우연히 발견해서 학습할 수 있었네요 ㅋㅋㅋ 👍🏻👍🏻
참고
https://swift-it-world.tistory.com/16
https://developer.apple.com/documentation/swift/array/removeall(keepingcapacity:)-1er5
https://developer.apple.com/forums/thread/702923
https://developer.apple.com/documentation/swift/array/capacity
https://stackoverflow.com/questions/27830470/swift-array-capacity-vs-array-count
아직 꼬꼬마 개발자입니다.
더 나은 설명이나 코드가 있다면 언제든 환영입니다.
적극적인 조언과 피드백 부탁드립니다!
그럼 오늘도 개발 가득한 하루되세요!
- Xerath -
🤖🤖🤖🤖🤖🤖🤖
'제라스의 iOS 공부 > Swift 지식' 카테고리의 다른 글
[Swift 지식] UITableView Cell의 생명주기(feat.prefetch를 써보자!) (0) | 2024.05.10 |
---|---|
[Swift 지식] @ObservedObject vs @StateObject 이 둘 언제 쓰는데? (feat.Observation) - (24.06.07 업데이트) (0) | 2024.05.05 |
[Swift 지식] @Bindable 딥다이브(였던 것) feat. 사실 Observable 딥다이브... (3) | 2024.04.07 |
[Swift 지식] Swift에서 프로토콜이 클래스를 상속한다고? (1) | 2024.03.31 |
[Swift 지식] Single Source Of Truth(SSOT)란? (0) | 2024.03.12 |