Array의 메모리 할당
배열은 메모리에서 특정한 영역에 연속적으로 저장됩니다. [1,2,3...]이라는 배열이 있다면 배열의 시작주소부터 연속적으로 요소들이 word단위로 저장됩니다.
1 | 2 | 3 | |
4 | 5 | 6 | |
이때 배열에 어떤 요소를 추가하면, 다음 주소에 다른 데이터가 저장 되 있을 수 있으므로, 메모리에서 배열을 저장 할 수 있는 공간을 찾아 할당하게 됩니다.
1 | 2 | 3 | 4 |
5 | 6 | 7 | |
Cat | |||
만약 어떤 요소를 추가할때마다, 이같이 메모리의 특정영역에서 저장할 공간을 찾는다면 성능적으로 문제가 있을 수 있습니다. 그래서 Swift에서 배열은 배열보다 더 큰 양의 메모리 공간을 예약합니다. 이때 예약된 공간도 다 사용하게 된다면 현재 배열의 배수의 크기만큼 공간을 찾아 예약을 하게 됩니다. 다만 무조건 배수의 크기만큼 예약하는게 아닌 배열의 크기에 따라 다른듯 합니다.
String의 성능
Swift String은 Character의 배열입니다. 따라서 +, +=, append, joined 같은 연산을 할 수 있고 그에따른 메모리 할당 매커니즘 역시 같습니다.
하지만 String은 struct이므로 어떤 연산을 하는지에 따라 성능의 차이가 나타납니다.
+ 연산
public static func + (lhs: String, rhs: String) -> String {
var result = lhs
result.append(rhs)
return result
}
String에서 +연산을 할 시 COW로 동작을 하므로, 새 객체를 만들어지고 메모리에 할당되게 됩니다. 따라서 +를 반복적으로 많이 하게 될 경우 성능 하락(메모리 할당 및 해제에 대한 비용)이 발생할 수 있습니다.
+= 연산
public static func += (lhs: inout String, rhs: String) {
lhs.append(rhs)
}
+= 연산은 새로운 인스턴스가 아닌 참조로 문자열을 추가하기에 +연산처럼 메모리에서 공간을 찾지 않아도 됩니다.
joined 연산
internal func _joined(separator: String) -> String {
let underestimatedCap =
(1 &+ separator._guts.count) &* self.underestimatedCount
var result = ""
result.reserveCapacity(underestimatedCap)
if separator.isEmpty {
for x in self {
result.append(x._ephemeralString)
}
return result
}
var iter = makeIterator()
if let first = iter.next() {
result.append(first._ephemeralString)
while let next = iter.next() {
result.append(separator)
result.append(next._ephemeralString)
}
}
return result
}
}
reserveCapacity()함수로 문자열의 크기만큼 메모리상에서 공간을 탐색한 후 문자열을 생성 및 할당합니다. 즉 한번만 메모리에서 공간을 찾는 작업을 하면 됩니다.
따라서 다음과 같은 코드가 존재한다면
var value = "123"
let arr = ["4","5","6","7","8"]
for i in arr {
value = value + i
value.append(i)
value += i
}
요로케 joined을 사용하는 방식이 성능적으로 최적화 된 코드라 할 수 있겠네요.
var value = "123"
let arr = ["4","5","6","7","8"]
value.append(arr.joined())
배열의 크기를 알기에 메모리에 공간을 할당을 하는 작업을 딱 한번만 하기 때문입니다. 반복문 코드에서는 반복적으로 배열에 값을 추가하기때문에 예약된 공간이 전부 다 차면 다시 메모리를 할당해야 되기 때문이죠.
'iOS > swift' 카테고리의 다른 글
Swift) lazy와 weak를 같이 쓸 수 없는 이유 (0) | 2023.08.29 |
---|---|
파라미터 Protocol 타입과 제네릭의 차이점 (0) | 2023.08.22 |
Property Wrapper (0) | 2023.07.29 |
경로의 크기에 맞추어 지도 이미지 생성하기 (MKMapView, MKMapSnapshotter) (0) | 2023.05.12 |
swift 참조 타입과 값 타입, 함수 파라미터의 관계 (0) | 2023.04.27 |