스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Xcode 빌드에서의 병렬 처리에 대한 오해 해소
Xcode 빌드 시스템이 빌드에서 최대 병렬 처리를 추출하는 방법을 알아보세요. 프로젝트를 구조화하여 빌드 효율을 개선하는 방법을 살펴보고, Xcode에서 타겟의 빌드 단계 간의 관계를 해결하기 위한 프로세스를 안내하며, Swift에서 컴파일 시 사용 가능한 하드웨어 리소스를 최대한 활용하는 방법을 공유합니다. 또한 빌드 효율과 성능을 모니터링하는 데 도움이 되는 강력한 도구인 Build Timeline을 소개합니다.
리소스
관련 비디오
WWDC22
-
다운로드
♪ ♪ 안녕하세요 WWDC 2022에 오신 것을 환영합니다 Xcode 빌드 시스템 팀의 엔지니어 Ben입니다 안녕하세요, Swift 컴파일러 팀의 엔지니어 Artem입니다 이 강연에서는 빌드 내부의 병렬화를 이해하기 위한 Xcode의 빌드 프로세스에 대해 자세히 알아볼 겁니다 Ben이 빌드의 핵심 개념에 대한 소개를 먼저 드리고 빌드 성능 문제를 검토하는데 도움이 되는 Xcode가 제공하는 사용 가능한 도구를 살펴볼 겁니다 그 다음, 타겟 빌드 시 Xcode의 병렬화 증가 방법을 보겠습니다 거기에 덧붙여 제가 Xocde가 어떻게 많은 타겟으로 구성된 프로젝트를 구축하면서 빌드를 전체적으로 병렬화하는지 설명하고 마지막으로 요점을 말씀드리죠 Ben, 말씀해주세요 Xcode에서 CMD+B 키를 눌러 앱을 빌드할 때 발생하는 일을 다시 보죠 Xcode의 일부로, 빌드 시스템은 전체 프로젝트의 표현으로 호출됩니다 모든 소스 파일, 에셋 파일 빌드 설정 및 실행 타겟과 같은 기타 구성을 포함하죠 빌드 시스템은 앱 빌드 방법에 대한 단일 정보 소스입니다 어떤 설정을 사용하여 어떤 도구를 호출할 지 최종적으로 앱을 만들기 위해 어떤 중간 파일을 생성할지 알고 있죠 다음 단계로 빌드 시스템은 컴파일러와 같은 프로젝트의 입력 파일을 처리하는 도구를 호출합니다
Clang과 Swift 두 컴파일러 모두 링커가 앱을 나타내는 실행 프로그램을 연결하는 데 필요한 개체 파일을 생성합니다 이 순서가 말이 되기는 하지만 어디서 왔는지 명확하지 않습니다 따라서 해당 프로세스의 한 예시와 빌드 시스템이 모든 작업의 실행 순서를 결정하는 법을 살펴봅시다
Swift 컴파일러는 입력 소스 파일을 사용해 프로그래머의 의도를 캡처하고 기계 실행 가능한 바이너리로 변환하며 도중에 오류가 있는지 소스 코드를 확인합니다 이 프로세스는 실패할 수 있고 실패 시 빌드가 취소되죠 그러나 성공 시엔 각 입력에 대한 개체 파일을 생성합니다 이런 개체 파일들은 링커를 호출하는데 사용됩니다 링커는 개체 파일들을 결합하고 실행 파일을 생성하기 위해 외부에 연결된 라이브러리에 대한 참조를 추가하죠 두 작업은 소비와 생산을 기반으로 종속성을 가집니다 컴파일러에 의해 생성된 개체 파일은 링커에 의해 소비됩니다 이로 인해 빌드 시스템 그래프에 종속성이 생성되죠 파일 콘텐츠 자체는 빌드 시스템에 중요하지 않지만 작업 간의 종속성은 중요합니다 빌드를 실행하는 동안에는 다른 작업의 입력을 생성하는 작업이 완료됐다는 확인이 되어야 해당 작업을 시작할 수 있습니다 이 핵심 개념은 모든 종류의 작업에 유효한데요 작업 A와 작업 B 사이의 종속성을 보여주는 보다 일반적 시각화를 보여드리죠 이 경우 A는 B의 입력 중 일부 또는 전체를 생성합니다
컴파일링과 링킹은 전체 타겟을 빌드하기 위해 실행해야 하는 많은 작업 타입 중 일부일 뿐이므로 그래프에 좀 더 일반적인 작업을 추가해보겠습니다 에셋 컴파일링, 파일 복사 또는 코드 서명과 같은 다른 타입의 작업을요 이들은 다함께 프레임워크 타겟의 빌드를 나타냅니다 이런 작업은 입력 및 출력을 기반으로 종속성을 정의한 것이죠 따라서 실행 작업 A를 완료하면 실행 작업 B, C의 차단이 해제되고 실행 작업 B를 완료하면 작업 D, E의 차단이 해제됩니다 차단이 해제된 작업을 '다운스트림'이라고 하며 차단하는 작업은 '업스트림'입니다 많은 프로젝트에는 둘 이상의 프레임워크 타겟이 포함되므로 앱과 앱 확장을 나타내는 두 개의 타겟을 더 추가해봅시다 타겟은 프로젝트에서 서로 간의 종속성을 명시적 또는 암시적 종속성을 통해 정의합니다 'Link Binary with Libraries' 빌드 단계에 추가되어
종속성을 정의하는 것과 같이요 이 경우 앱은 앱 확장을 포함하고 프레임워크에 링크됩니다 앱 확장은 이 프레임워크를 사용하고 있지 않기 때문에 종속 관계가 없죠
빌드 그래프를 실행할 때 다른 작업엔 다른 시간이 걸립니다 작업을 완료하는데 필요한 복잡성 수준으로 귀결되죠 입력의 크기과 필요한 계산에 따라 달라집니다 많은 파일의 컴파일링은 일반적으로 몇 개의 헤더 파일 복사 보다 훨씬 시간이 더 걸립니다 이를 고려하면 다음과 같이 되죠 빌드 시스템이 다음와 같은 빌드를 실행할 때 종속성이 없는 작업을 먼저 실행하며 시작합니다 이것들이 완료되고 나서야 다운스트림 작업의 차단을 해제해나가죠 계획된 모든 작업이 완료될 때까지 이 프로세스를 따릅니다
뒤따르는 빌드에서는 출력이 최신 상태인 동안 입력이 변경되지 않는 작업은 빌드 시스템이 건너뛸 수 있죠 변경된 입력으로 인해 작업을 다시 실행해야 하는 경우 앱 타겟의 B와 같은 경우는 출력이 변경된 경우 다운스트림 작업도 재실행해야하죠 다른 모든 작업을 건너뛸 수 있다면 프로젝트의 처리 시간이 반복적인 작업 시 매우 빨라집니다 이를 증분 빌드라고 하죠 하지만 지금은 전체 빌드를 사용하겠습니다
작업 실행의 종속성과 소요 시간은 첫 다운스트림 작업을 시작할 수 있는 시간을 정의합니다 이 정보를 사용하여 임계 경로를 계산할 수 있죠 이론적으로 무한한 리소스가 있을 때 빌드 실행에 필요한 가장 짧게 걸리는 시간을 의미하죠 이 강연 전반에서 보게 될 공통 패턴은 이 경로를 단축하여 병렬화 및 확장성이 뛰어난 빌드 그래프를 생성하는 겁니다 더 짧은 임계 경로가 반드시 전체 빌드 시간을 단축하는 것은 아닙니다 하지만 빌드가 하드웨어와 함께 확장되도록 하죠 빌드 임계 경로는 빌드 속도를 제한하는 요소들을 정의합니다 하드웨어에서 허용할지라도 빌드를 더 빨리 완료할 수 없죠 임계 경로 단축은 그 안에 있는 종속성을 분해하여 수행됩니다 빌드가 어떻게 수행됐는지 살펴보고 그 빌드 실행에 대해 더 많은 이해를 하려면 실행 시간 기준의 데이터 플롯이 필요하죠 너비는 작업의 길이를 나타냅니다 이 둘과 같이 넓은 요소는 오래 실행되는 작업을 나타내고 이들과 같은 좁은 요소는 빨리 마무리되는 작업을 나타냅니다
그래프의 높이는 주어진 시간에 병렬로 실행되는 작업의 수를 나타냅니다 CPU 또는 메모리 사용률과 직결되진 않는 것을 유의하세요
빈 공간은 이 두 시나리오에서처럼 다운스트림 작업을 막는 작업들로 인해 발생합니다 마지막으로 요소의 색상은 연관 타겟을 나타내죠 Xcode 14의 신기능인 이 시각화를 발표하게 되어 매우 기쁩니다 빌드가 완료된 후 성능을 이해하는 데 도움이 되죠 Xcode 빌드 타임라인은 빌드 로그에 추가된 새 기능입니다 계층 구조가 아닌 병렬화를 기반으로 시각화하여 빌드 성능을 이해합니다 주어진 시간 동안의 행 수는 해당 시간 동안의 병렬 처리 수준을 나타냅니다 개별 작업의 가로 길이는 작업을 완료하는 데에 필요한 기간을 나타내죠 그래프의 빈 공간은 완료되지 않은 작업이 다운스트림 작업 실행을 차단한 위치를 보여줍니다 타임라인 요소에 적용된 다양한 색상은 빌드의 일부였던 다양한 타겟을 구별하는데 도움을 줍니다 증분 빌드에서는 타임라인에 실제로 실행된 작업만 포함되어 장기 실행 작업을 발견할 수 있도록 합니다 특히 이 빌드 중에 실행될 것으로 예상되지 않았을 수 있는 것들을요
다음은 Xcode 14의 빌드 타임라인 데모입니다 이 창에 제가 Github에서 문서 컴파일러를 빌드하는 Swift-DocC 프로젝트 사본을 열었습니다 Scheme을 위해 빌드된 타겟에 대한 개요를 보기위해 scheme 편집기를 확인해봅시다 먼저 scheme을 클릭하고 'scheme 편집'을 선택합니다
'build' 탭에는 모든 타겟 목록이 포함되어 있습니다 타겟은 명시적으로 scheme에 추가하거나 이미 scheme의 일부인 타겟의 종속성으로 암시적으로 추가할 수 있습니다 이 경우 자동으로 생성된 scheme과 함께 Swift 패키지를 사용하고 있습니다 매니페스트의 모든 타겟들은 따라서 명시적으로 정의됩니다
이 로그는 이전에 실행한 해당 scheme의 빌드를 나타냅니다 빌드 시스템이 실행한 모든 작업에 대한 항목을 포함하죠 항목들은 계층 구조로 정리됩니다 속해 있는 타겟을 기반으로요 여기의 'docc' 타겟과 같이 해당 타겟의 실행 파일을 성공적으로 빌드하기 위해 Xcode는 이 노드의 자식이 나타낸 모든 작업을 실행했습니다 현재 빌드 로그는 'All'이 선택 되었으므로 증분 빌드에서 다시 실행할 필요가 없던 이전 빌드의 작업도 표시됩니다 'Recent'를 선택하면 실제로 실행된 작업만 표시되고 건너뛴 작업은 모두 숨겨집니다 그 외에도 빌드 로그는 필터도 지원해서 문제가 있거나 실패한 작업만 표시할 수도 있죠
이 빌드에 대한 빌드 타임라인을 열려면 편집기 옵션으로 이동해 도우미를 엽니다 그러면 빌드 타임라인이 빌드 로그 옆에 열립니다 평소와 마찬가지로 편집기 옵션은 오른쪽 또는 아래쪽 도우미 표시 설정을 제공합니다 저는 아래에 두겠습니다 타임라인은 빌드의 병렬화를 기반으로 동일한 데이터를 '최근' 빌드 로그로 시각화합니다 하나에서 한 요소를 선택하면 다른 데에서도 요소가 선택됩니다 이를 통해 맥락적으로 작업 실행을 볼 수 있죠 여기 타임라인은 선택한 작업과 병렬로 실행된 작업에 대한 감을 줍니다 트랙패드에서 손가락을 오므려서 다시 축소했습니다
타임라인에서 요소를 선택하면 빌드 로그에 표시됩니다 빌드 로그는 계층 구조를 기반으로 시각화하기 때문에 컴파일러 호출의 일부로 어떤 파일이 컴파일 되었는지 볼 수 있습니다 또한 해당 호출의 전체 명령 라인을 볼 수 있죠
Option 키를 누를 상태에서 빌드 타임라인의 영역을 선택하면 타임프레임에 맞게 뷰 포트가 조정됩니다 여기 보시면 타겟 ArgumentParser 링크가 동일한 타겟의 컴파일을 기다리고 있는 것을 확인할 수 있죠 Option 키를 누른 상태에서 위로 스크롤하면 빠른 축소가 가능합니다 타임라인의 행 수는 해당 시점에 병렬로 실행된 작업의 수를 나타냅니다 빈 공간은 작업이 생성되지 않은 입력을 기다림을 나타내죠 이상적인 타임라인은 가능한 빈 공간이 적게 세로로 채워집니다 이렇게 하는 것이 빌드 그래프가 가장 잘 확장되고 빌드가 빨라지며 이에 따라 하드웨어도 빨라지죠 이를 달성하기 위해 Xcode는 올해 많은 개선 사항을 제공합니다 임계 경로 단축을 위해서요 이제 Xcode가 개별 타겟을 정의하고 빌드하는 방법 및 병렬화를 증가시키는 방법에 대해 살펴보죠 타겟을 구성할 때, 빌드 단계는 해당 타겟의 제품 생산을 위해 수행해야하는 작업을 설명합니다 이들은 프로젝트 편집기에 정의되며 소스 파일 세트 컴파일 해야하는 에셋 및 헤더 또는 리소스와 같은 복사해야 하는 파일 그리고 링크되는 라이브러리나 실행되는 스크립트를 포함합니다 많은 빌드 단계는 다른 빌드 단계의 입출력 작업을 설명하며 그들 사이에 종속성을 생성합니다 예를 들어 타겟의 소스 파일은 링크되기 전에 컴파일되어야 하죠 하지만 이것이 모든 빌드 단계에 적용되는 것은 아닙니다 각 빌드 단계의 작업들을 일렬로 실행하는 대신 빌드 시스템은 빌드 단계의 입력과 출력을 고려해 병렬로 실행이 가능한지 결정합니다 예를 들어 컴파일과 리소스 복사는 병렬로 실행할 수 있습니다 서로 어느 쪽도 다른 쪽의 출력에 의존하지 않기 때문이죠 하지만 링킹은 컴파일을 따라야 합니다 컴파일 단계에서 생성된 개체 파일에 의존하기 때문이죠 이제 'Run Script' 빌드 단계가 포함된 다른 타겟을 보겠습니다 다른 빌드 단계와 달리 스크립트 단계의 입출력은 타겟 편집기에서 수동으로 구성해야 합니다 결과적으로 빌드 프로세스에서 데이터 경쟁이 발생하지 않도록 빌드 시스템은 연속적 스크립트 단계를 한 번에 하나씩 실행합니다 만약 타겟의 스크립트가 종속성 분석을 기반으로 실행되고 입력 및 출력의 전체 목록을 지정하도록 구성된 경우 FUSE_BUILD_SCRIPT_PHASES 빌드 설정을 YES로 설정하여 빌드 시스템이 병렬로 실행 시도를 해야 함을 나타낼 수 있습니다 하지만 스크립트 단계를 병렬로 실행할 경우 빌드 시스템은 지정된 입력과 출력에 의존해야 하죠 따라서 스크립트 단계에서 불완전한 입력 또는 출력 목록은 디버그가 매우 어려운 데이터 경쟁으로 이어질 수 있는 점 유의하세요 이를 완화하기 위해 Xcode는 사용자 스크립트 샌드박싱을 지원해 각 스크립트 단계의 종속성을 정확하게 선언할 수 있도록 합니다 샌드박싱은 셸 스크립트가 실수로 소스 파일 및 중간 빌드 객체에 액세스하는 것을 차단하는 선택적 기능입니다 이런 항목이 단계의 입력 또는 출력으로 명시적으로 선언되지 않는 한 이 예시에서는 입력 및 출력.txt 파일이 모두 해당 스크립트 단계에 대한 종속성으로 선언되지 않았으므로 샌드박스는 프로젝트를 빌드할 때 스크립트가 두 파일을 읽고 쓰는 것을 차단합니다 스크립트가 샌드박스를 위반하면 0이 아닌 종료 코드와 함께 실패하여 빌드에 실패하게 되죠 그 외에도 Xcode는 스크립트 단계가 제대로 선언을 하지 않고 액세스를 시도한 모든 경로를 나열합니다 두 파일을 이 스크립트 단계에 종속성 정보로 추가하면 이 문제가 해결됩니다 이런 방식으로 샌드박스는 스크립트가 선언된 입출력 이외의 파일에 실수로 액세스하지 않도록 합니다 그럼 둘 이상의 스크립트 단계가 있는 예시를 살펴보면서 샌드박싱이 데이터 경쟁 및 잘못된 빌드를 방지하는 방법을 살펴보죠 두 스크립트 단계가 있습니다 첫 번째는 텍스트 파일을 읽고 내용의 체크섬을 계산하며 그 값을 DERIVED_FILE_DIR의 중간 파일에 씁니다 다른 스크립트는 동일한 텍스트 파일과 생성된 체크섬을 읽고 나중에 앱에 표시하기 위해 이들을 html 파일에 삽입합니다 이런 단계에 대한 정확한 입출력 종속성 세트가 선언되지 않은 경우 Xcode는 두 스크립트를 병렬로 실행합니다 FUSE_BUILD_SCRIPT_PHASES가 켜져있을 때요 그럼 이 문제의 시나리오를 자세히 살펴보죠 'HTML 생성' 단계에서 'checksum.txt'의 입력 선언이 누락되고 그 외에 두 스크립트의 다른 모든 입력과 출력은 올바르게 선언되었다고 가정합시다 샌드박싱이 없으면 이 잘못된 구성은 눈에 띄지 않으며 빌드에 문제를 일으킬 수 있죠 그 말은 Xcode가 두 단계 사이의 종속 관계를 추론하지 못하고 이 둘을 병렬로 실행하도록 예약한다는 것을 의미합니다 FUSE_BUILD_SCRIPT_PHASES가 켜져있을 때요 여기엔 몇 가지 위험 요소가 존재합니다 checksum.txt는 'HTML 생성'에 대한 입력 종속성으로 목록되지 않았기 때문에 클린 빌드 동안 스크립트는 파일 읽기를 시도하고 이는 파일 시스템에서 사용할 수 없죠 또 다른 위험은 checksum.txt가 디스크에서 사용 가능할 때 입니다 이전 실행 '체크섬 계산' 단계 때문에 'HTML 생성' 단계에서 두 스크립트가 병렬로 실행될 시 오래된 파일을 선택할 수 있습니다 이것은 사용자 오류로 샌드박스에서 스크립트를 실행하면 이 문제 방지에 도움이 됩니다 샌드박싱이 켜져 있는 상태에선 'HTML 생성'이 'checksum.txt'를 읽으려고 시도하는 그 즉시 실패하게 되고 오류 메시지는 해당 빌드 단계에 누락된 입력을 추가하도록 안내하죠 입력 및 출력을 올바르게 정의하면 Xcode가 두 단계 간 종속성 관계를 존중하도록 안내해서 '체크섬 계산' 단계가 'HTML 생성' 전에 실행되도록 합니다 관련 없는 빌드 단계는 여전히 병렬로 실행할 수 있습니다 타겟에 대해 샌드박스가 적용된 셸 스크립트를 활성화하려면 ENABLE_USER_SCRIPT_SANDBOXING 설정을 빌드 설정 편집기 또는 xcconfig 파일에서 YES로 선택합니다 요약하면, 샌드박스 쉘 스크립트를 사용하면 더 빠르고 강력한 증분 빌드를 가능하게 하는 올바른 종속성 정보를 가질 수 있습니다 입력 변경이 없고, 출력이 여전히 유효한 경우, 빌드 시스템이 스크립트 단계를 건너뛸 수 있는 확신을 가지기 때문이죠 그렇지 않은 경우엔 스크립트를 다시 실행합니다 스크립트 타겟에 대한 빌드 설정을 활성화하면 프로젝트의 소스 루트 내부 파일 및 파생된 데이터 디렉토리에 대한 액세스가 차단됩니다, 프로젝트 에서 스크립트의 입력 또는 출력이 명시적으로 정의되지 않은 경우에 샌드박스는 다른 디렉토리에 대한 무단 액세스를 방지하지 않으므로 이를 보안 기능으로 간주해선 안됩니다 이 기능을 사용하면 기존 스크립트 단계의 누락된 입력 또는 출력을 디버그하는데 도움이 되며 유효나 구성을 보장할 수 있죠 또한 앞서 설명한 빌드 설정 FUSE_BUILD_SCRIPT_PHASES와 함께 샌드박싱을 통해 바르게 정의된 종속성 엣지를 갖는 스크립트 단계는 빌드의 임계 경로를 줄이기 위해 병렬로 실행할 수 있습니다 여기까지가 타겟 빌드 단계 병렬화에 대한 것입니다 이제는 Artem이 많은 타겟을 빌드할 때의 병렬화를 쉽게 설명드릴 겁니다 Ben, 고마워요 이제 프로젝트에서 타겟 빌드에 들어가는 빌드 시스템 작업 및 단계의 기본에 대해 다루었으므로 좀 더 글로벌한 관점에서 Xcode가 Swift 타겟 간의 종속성을 사용하여 빌드에서 최대 병렬 처리량을 추출하는 방법을 살펴보고 프로젝트의 구조 및 구성이 빌드 시간에 미치는 영향도 알아봅시다 프로젝트를 구성하는 계층 구조에는 여러 수준이 있을겁니다 예를 들어 로컬 라이브러리 모음에 의존하는 앱 타겟은 의미론적 경계를 따라 여러 타겟 및 여러 프레임워크로 분할됩니다 각 타겟은 다양한 빌드 단계와 절차들을 포함하며 다른 타겟 빌드 단계로 그리고 빌드 단계로부터 파일 종속성을 생성하고 사용합니다 프로젝트의 크기가 커지면 이런 작업 그래프의 크기와 복잡성도 커지죠 Xcode 빌드 시스템이 이런 계층 구조를 평면화하면서 빌드를 무수한 작업들로 분할하고 이런 무수한 작업들은 모든 타겟의 빌드 단계에 해당됩니다 Swift 타겟에 특화된 한 종류의 작업은 컴파일입니다 Swift 타겟의 소스 코드를 바이너리 제품으로 빌드하는 것은 일반적으로 빌드 계획이나 컴파일 및 연결을 위한 많은 하위 작업을 포함하는 복잡한 작업이죠 이러한 작업의 조직화는 Xcode 도구 체인의 특수 도구인 Swift 드라이버에 위임됩니다 드라이버는 타겟 소스 코드에 필요한 컴파일러 및 링커 호출을 구성하는 시기와 방법에 대한 전문지식을 가지고 있죠 Swift 코드를 포함하는 모든 타겟은 또한 코드 배포의 단위인 모듈에도 해당합니다 이 타겟의 공개 인터페이스를 캡쳐하는 바이너리 모듈 파일은 다운스트림 타겟이 컴파일을 시작하는데 필요한 빌드 제품입니다 여러 타겟 중 하나를 빌드하기 위해 Swift 드라이버가 수행하는 작업의 한 예시를 자세히 살펴보죠 타겟은 하나의 소스 파일 모음으로 구성될 겁니다 릴리스 또는 최적화된 빌드에서 드라이버는 모든 소스 파일이 최적화 기회를 최대화하도록 하는 하나의 컴파일러 작업을 예약하죠 이 단일 컴파일 작업 또한 타겟의 Swift 모듈을 생성합니다 디버그 또는 증분 컴파일 모드에서 Swift 드라이버는 필요한 컴파일 노력을 병렬로 실행할 수 있는 더 작은 하위 작업으로 나눕니다 이 중 일부는 증분 빌드에서 다시 실행할 필요가 없죠 그 다음 Swift 모듈을 생성하려면 각 컴파일 작업의 부분적인 중간 제품을 병합하는 추가 단계가 필요합니다 만약, 이 예시처럼 타겟에 있는 소스 파일의 수가 많다면 개별 파일은 일괄 컴파일 하위 작업에 할당될 수도 있습니다 빌드 시스템의 휴리스틱에 따라서요 빌드 로그는 일괄 컴파일 작업에 할당되는 소스 파일을 강조표시하고 각 파일의 진단에 대해 별도의 항목을 사용합니다 다양한 소스 파일에 걸쳐 타겟의 빌드를 병렬화할 수 있는 것은 더 빠르고 더 작은 증분 빌드에 중요하기 때문에 디버그 빌드가 증분 컴파일 모드 설정을 사용하는지 확인하세요 Xcode 14 이전에는 Xcode 빌드 시스템과 Swift 드라이버 사이의 경계로 인해 타겟 빌드 단계의 오케스트레이션 및 드라이버의 각 타겟 인스턴스에 의해 생성된 컴파일 하위 작업이 서로 독립적으로 발생했습니다 각 구성 요소는 사용 가능한 시스템 리소스의 최대 활용을 위해 최선을 다했죠 이 예제 빌드 그레프를 사용해 각각의 컴파일 단계 일정 관리에 대해 자세히 살펴보겠습니다 앞서 배웠듯이 Swift 타겟 종속성은 종속 항목이 공개 인터페이스를 캡쳐하는 바이너리 모듈 파일을 제공하도록 함으로써 해결됩니다 이런 종속성 관계를 해결하면 다음과 같은 순서로 이어집니다 각 타겟에 대한 최상위 Swift 드라이버 작업 및 각 타겟의 개별 하위 작업이 타임라인에 나타나있죠 Xcode 14에서는 Swift로 작성된 완전히 새로운 Swift 드라이버의 구현 덕분에 빌드 시스템과 컴파일러가 완전히 통합되었습니다 Xcode 빌드 시스템은 코드 컴파일을 위해 수행해야 하는 모든 작업의 중앙 일정 관리자 역할을 합니다 이 중앙 계획 매커니즘을 통해 Xcode는 세밀한 일정 결정을 내릴 수 있고 프로젝트를 빌드할 때 사용 가능한 리소스 만큼만 사용하도록 보장합니다 CPU 초과 구독과 전체 시스템 성능 저하 없이요
또한 이전에는 Xcode 빌드 시스템 범위를 벗어났던 하위 작업 조각들의 모음이 이제 완전히 빌드 시스템 스케줄러 도메인에 있습니다
모든 개별 하위 작업이 중앙 작업 풀에 있게 되면서 빌드 스케줄러가 만드는 상호 보완을 고려하는 것이 중요합니다 예를 들어, 8-코어 시스템에서의 스케줄러 기본값은 사용 가능한 작업 즉, 종속성이 충족되어 이동할 준비가 된 작업을 8개의 사용 가능한 실행 슬롯 중 하나에 할당하는 겁니다 슬롯 중 하나가 비는 즉시 빌드 시스템은 아직 미처리된 작업으로 슬롯을 채우려 시도하죠 코어 수가 많은 시스템에서는 더 많은 동시 작업을 수행할 수 있습니다 이는 곧 더 많은 작업을 수행할 수 있는 유휴 코어를 가질 가능성이 더 높다는 것을 의미하기도 하죠 그렇지만 현재 진행 중이거나 대기 중인 다른 작업에 의해 생성된 모든 미처리된 작업은 여전히 입력을 기다립니다 새로운 통합 빌드 시스템은 스케줄러가 이러한 유휴 시간을 크게 줄일 수 있도록 합니다 그 방법을 보기 위해 컴파일에 대한 타겟 종속성인 바이너리 모듈 파일이 어떻게 해결되는지를 다시 살펴보죠
이전에 다루었듯이 하위 작업 컴파일의 부분적 결과들은 타겟의 최종 모듈 제품에 병합됩니다 제품이 사용 가능하게 되면 다운스트림 타겟이 컴파일을 시작할 수 있죠 Xcode 14 및 Swift 5.7에서는 새롭게 타겟 모듈의 구성이 모든 프로그램 소스 파일에서 직접 별도의 모듈-방출 작업에서 수행됩니다 이는 타겟의 종속성이 모듈 방출 작업이 완료되는 즉시 다른 컴파일러 작업을 기다리지 않고 컴파일을 시작할 수 있음을 의미하죠 다운스트림 타겟 컴파일 차단을 더 빨리 해제할 수 있게 되면 빌드 타임라인의 활동 사이의 빈 공간인 유휴 CPU 코어로 사용 가능한 작업을 기다리는 시간을 줄일 수 있죠
이를 나머지 프로젝트로 확장해보면 전반적으로 비슷한 양의 작업을 수행하고 있지만 빌드 시스템은 컴퓨터의 리소스를 더 효율적으로 사용할 수 있고 종종 빌드를 훨씬 더 빠르게 완료할 수 있습니다
이제 빌드 시스템이 Swift 빌드 시에 수행할 수 있는 두 번째 교차-타겟 최적화 기능인 즉시 링킹에 대해 살펴보겠습니다 이전 예시를 기반으로 각 타겟에 대해 링커 작업들을 추가했는데요 모두 빌드의 임계 경로에 있습니다 이 경우 타겟 B가 타겟 A를 연결하기 때문에 타겟 B의 링크 작업은 타겟 A의 링크된 출력이 생성되고 자체 컴파일 작업이 완료될 때까지 기다려야 하죠 그러나 즉시 링킹을 사용하면 타겟 B의 링크 작업이 타겟 A의 모듈-방출 작업에 의존할 수 있고 그 결과 타겟 B는 링킹을 빌드 초기에 시작할 수 있습니다 타겟 A와 병렬로 실행하며 임계 경로를 단축할 수 있죠 이것이 어떻게 작동할까요 일반적으로 링크된 제품 종속성이 있는 두 타겟의 종속성 그래프는 다음과 같습니다 종속 타겟을 링킹하려면 타겟 자체의 컴파일 출려과 함께 자체 종속성들이 링크된 제품이 필요합니다 즉시 링킹을 하면 이 종속성이 깨지면서 종속 타겟이 더 일찍 링킹 될 수 있도록 하죠 종속성이 링크된 제품에 의존하는 대신 이전 빌드 프로세스에서 모듈-방출 작업에 의해 생성된 텍스트 기반 동적 라이브러리 스텁에 의존합니다 이 스텁에는 종속성이 사용할 수 있도록 링크된 제품에 심볼 목록이 포함되어 있죠 Xcode 빌드 설정을 사용해 이 최적화를 활성화할 수 있습니다 즉시 링킹은 종속 항목에 의해 동적으로 링킹된 모든 순수 Swift 타겟에 적용됩니다 요약하자면, Xcode 빌드 시스템은 정교한 스케줄링 엔진으로 빌드 단계를 병렬로 실행하여 가능한 많은 병렬성을 추출합니다 스크립트 샌드박싱과 같은 기능을 사용하면 빌드가 최대한 병렬화되고 신뢰할 수 있도록 보장할 수 있죠 Xcode와 Swift는 그 어느 때보다 통합되었습니다 그리고 프로젝트의 구조, 즉 모듈화, 타겟 제품 사이의 종속성으로 구성된 전체적 그래프 모양 및 그 안에 있는 빌드 단계의 수와 복잡성 그리고 여러분의 기기에서 사용 가능한 계산 자원들 이 모든 것이 Xcode가 빌드를 병렬화하고 속도를 높일 수 있는 정도에 기여하는 요소들이죠 이에 대한 지식과 강력한 새 도구 빌드 타임라인을 사용하면 프로젝트를 검토하고 빌드에 대한 통찰력을 얻을 준비를 마쳤습니다 만약 이면의 기술적 세부 사항에 대해 더 자세하게 알고 싶다면 저희가 설명드린 Xcode에서 사용되는 많은 기술은 오프 소스에서 개발되었기 때문에 GitHub의 Swift 드라이버 저장소는 아래 링크에서 찾을 수 있습니다 Xcode에 대한 더 멋진 세션을 보려면 'Xcode의 새로운 기능' 강연에서 올해의 모든 새로운 기능 및 개선 사항을 확인하세요 그리고 '빠른 링크: 빌드 및 실행 시간 개선' 강연을 통해 Xcode 14 링커가 링크 시간을 최대 2배 향상시킨 방법을 알아보세요 따라와 주셔서 감사합니다 Xcode 빌드에 대한 새로운 인사이트를 얻으셨기를 바랍니다 여러분이 어떤 작품을 만들지 기대가 됩니다 남은 컨퍼런스도 잘 마무리하시길 바랍니다
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.