본 글은 제가 https://developer.apple.com/videos/play/wwdc2024/10217/ 영상을 보고 느낀 점을 정리한 내용입니다
초입
C언어 개발자는 대부분 코드를 보면 성능을 직관적으로 알 수 있다.
malloc, 지역변수 등이 어떻게 동작될지 보이기 때문이다.
하지만 Swift는 그렇지 않다.
C, Swift 두 언어 모두 개발해본 입장으로 이 부분은 명확하다. C에서는 코드가 내부적으로 어떻게 동작할지 보인다.
공통점을 찾자면 swift, C언어 둘다 llvm을 기반으로 기계어로 번역되기 때문에 java에 비해 성능적인우위가 있다.
반면에 Swift는 안전성, 유저 편의를 위해 추상화가 되어있으므로 내부적으로 어떻게 동작되는지 추론하기가 더 힘들다.
https://demian-develop.tistory.com/30 (하지만 대부분 예측 가능한 정도이다.. string을 제외하면..)
현대에 들어서 하드웨어가 좋아졌기 때문에 이 정도로 코어한 곳에서 문제가 발생하기는 쉽지 않다. + (컴파일러가 해주는 최적화)
하지만 이런 부분들을 알아놓으면 불필요한 리소스 낭비를 예방 할 수 있거나, 때때로, 성능 이슈를 개선할 수 있으므로 알아놓자
성능
- 보통 앱 개발자는 , UI가 느리거나, 배터리가 너무 많이 닳거나, 앱이 crash 되거나 할때 성능문제에 관심을 가진다.
- 컴파일러는 만능이 아니기 때문에 코드 작성 방식에 따라 컴파일러 최적화가 달라질 수 있다.
- low-level에서 성능은 보통 4가지에 좌우된다.
1. 최적화되지 않은 많은 호출
2. 데이터가 표현되는 방식
3. 메모리 할당 시간
4. 값 복사
여기선 함수 호출과 관련해 dynamic/staic dispatch를 이야기한다. 2016 Understanding Swift Performance에도 나왔던 이야기 인데, 즉, 컴파일 시점에 호출하는 함수를 명확히 알 수 있냐 없냐에 따라 성능이 살짝 다르다.
static dispatch가 테이블을 만들지 않기 때문에 더 빠르다. 물론 현대 모바일 기기에서 이 정도 최적화 유무로 이슈가 발생하진 않겠지만
여러곳에서 이런 작은 차이가 쌓이면 충분히 체감할 수 있을 것이라 생각한다.
이 부분은 protocol을 사용하거나, class 상속을 이용한다면 어차피 발생할 수 밖에 없겠지만.. 그래도 상속이 필요 없다면 class 앞에는 final을 붙여주자
메모리 할당
글로벌 메모리 - 프로그램이 로드될 때 할당되고 초기화된다.
스택 메모리 - 로컬 상태를 위한 메모리 할당이다
힙 메모리는 - 매우 유연하다. 임의의 시간에 할당하고 임의의 시간 후에 해제할 수 있다.
swift는 ARC를 통해 이를 관리한다.
기존 운영체제에서 이야기하고 있는 프로세스 메모리구조를 swift에서 조금 더 단순히 이야기한다
global memory의 경우 lazy로 초기화되는것을 알 수 있는데, 이는 여기서 말한것처럼 일단 메모리에 로드되면 앱 구동부터 앱 종료까지 메모리를 계속 차지하고 있기 때문이다. 그 뒤에 stack, heap은 class, struct와도 밀접한 관련이 있다.
메모리 레이아웃
- Swift에선 고수준에서 Value Type을 정의하기 때문에 Array도 Value로 보나 이는 메모리 레벨에서 혼동을 줄 수 있다.
- inline이란 어떤 포인터도 거쳐가지 않고 얻을수 있는 정보를 의미한다.
- 따라서 Swift에서 Array의 inline표현은 8바이트 포인터이다
- struct, tuple, enum은 inline 저장이고, class, actors는 out-of-line 저장을 한다.
- arrary는 Value타입이기 때문에 자주 복사가 될 수 있다. 이런 점 때문에 consume 연산자가 나왔다.
이 부분은 아마 Swift만 개발했던 사람이라면 헷갈릴 수 있는 부분이다.
많은 언어는 Array가 Refernce Type이기 때문에 다른언어를 하다 온 사람이라면 Swift에서 Array가 Value Type인게 오히려 이상하다고 느낄 수 있다. (C레벨에선 String, 동적 크기를 가진 Array 등은 힙에 저장될 수 밖에 없는것을 직관적으로 알 수 있다.)
consume 연산자는 좀 새로웠다. 생각보다 성능에 관심을 갖는 사람들이 많았나보다.
consume 키워드를 통해 불필요한 메모리 낭비를 막을 수 있다.
뒤는.. async/await에서 스택메모리, generic/ protocol 컴파일러 최적화 등인데
이쪽에서 얻어갈 정보는 "protocol과 generic 둘다 사용가능하다면 컴파일 타임에 타입 추론이 가능한 generic을 써야 최적화가 된다" 이다
시간날 때 글 한번 다듬으면서 같이 적어야겠다.
reference
'iOS > Swift' 카테고리의 다른 글
iOS) Swift의 Time complexity에 관한 고찰 (9) | 2021.02.12 |
---|---|
iOS) Swift에 대한 고찰 (0) | 2021.02.02 |