Cắt một bản build Xcode 4 phút xuống 50 giây
Một bản build sạch mất bốn phút. Một bản build tăng dần sau khi đổi một dòng mất chín mươi giây, đủ lâu để mất mạch suy nghĩ và mở một tab trình duyệt.
Phần lớn hóa ra là do bộ kiểm tra kiểu của Swift, và việc tìm ra dòng nào chịu trách nhiệm chỉ tốn một thiết lập build.
Đo trước đã
Thêm mấy dòng này vào Other Swift Flags trong build settings:
-Xfrontend -warn-long-function-bodies=100
-Xfrontend -warn-long-expression-type-checking=100
Nó phát ra một cảnh báo cho mọi thân hàm hay biểu thức mất hơn 100ms để kiểm tra kiểu. Hãy build, rồi sắp xếp trình điều hướng vấn đề theo cảnh báo đó.
Lần đầu tôi làm việc này, một biểu thức mất 8,4 giây. Chỉ riêng nó. Trong một file tôi sửa hằng ngày.
Ba khuôn mẫu gây ra phần lớn
Số học trộn kiểu trong một biểu thức. Đây là cái lớn nhất:
// 8,4 giây
let spacing = (width - padding * 2 - CGFloat(items.count - 1) * gap) / CGFloat(items.count)
Mọi hằng số học trong Swift đều là generic trên ExpressibleByIntegerLiteral, và mọi toán tử đều
được nạp chồng. Bộ kiểm tra kiểu phải cân nhắc mọi tổ hợp của Int, Double, CGFloat và phần còn
lại cho từng biểu thức con, và không gian tìm kiếm tăng theo hàm mũ so với số hạng tử.
Chẻ nó ra với kiểu tường minh làm sập cái không gian tìm kiếm đó:
// 40ms
let count = CGFloat(items.count)
let totalGaps: CGFloat = (count - 1) * gap
let available: CGFloat = width - padding * 2 - totalGaps
let spacing: CGFloat = available / count
Cùng phép tính, bốn câu lệnh, biên dịch nhanh hơn hai trăm lần. Nó cũng đọc dễ hơn, thứ khiến tôi tự thuyết phục mình rất nhanh.
Hằng tập hợp không có chú thích kiểu.
// chậm: bộ kiểm tra phải suy ra kiểu phần tử từ mọi phần tử
let config = ["timeout": 30, "retries": 3, "backoff": 1.5]
// nhanh
let config: [String: Double] = ["timeout": 30, "retries": 3, "backoff": 1.5]
Chuỗi + dài trên chuỗi ký tự hay mảng. Vẫn là vấn đề của số học — + bị nạp chồng rất nhiều.
Nội suy chuỗi vừa biên dịch nhanh hơn vừa dễ đọc hơn.
Mẹo
Quy tắc bao phủ cả ba: hãy chú thích kiểu khi một biểu thức có hơn khoảng ba hạng tử, hoặc khi
nó trộn các kiểu số. Suy luận kiểu là một tiện nghi, và là một tiện nghi đắt lên nhanh hơn
tuyến tính. Viết thêm : CGFloat chẳng tốn của bạn gì.
Chia module, và nó thật sự đem lại gì
Chẻ ứng dụng thành các Swift package cục bộ là lời khuyên ai cũng đưa ra. Nó có ích, và ít hơn tôi tưởng.
Cái lợi là Xcode không build lại một module mà mã nguồn không đổi, nên sửa một view chỉ build lại module tính năng đó và ứng dụng. Trên codebase này, build tăng dần đi từ 90 giây xuống khoảng 25.
Cái giá là có thật: các package cục bộ cần khai báo phụ thuộc tường minh, preview đôi khi hỏng theo những cách khó chẩn đoán, và một phụ thuộc vòng giữa hai module tính năng là một cuộc tái cấu trúc chứ không phải một cảnh báo.
Lời khuyên của tôi là hãy làm phần bộ kiểm tra kiểu trước. Nó tốn vài giờ, không cần thay đổi kiến trúc nào, và trên codebase của tôi nó là cái lợi lớn hơn trong hai cái.
Các thiết lập build đáng kiểm tra
SWIFT_COMPILATION_MODE nên là incremental cho Debug và wholemodule cho Release. Nếu Debug
bị đặt thành whole-module thì mọi thay đổi đều build lại mọi file trong module — tôi tìm thấy điều
này trên một dự án mà ai đó đã đặt nó toàn cục để “làm Release nhanh hơn”.
DEBUG_INFORMATION_FORMAT nên là dwarf cho Debug, không phải dwarf-with-dsym. Sinh ra một
dSYM ở mọi bản build debug là lãng phí thuần túy; bạn chỉ cần nó để giải mã ký hiệu cho bản phát
hành.
ONLY_ACTIVE_ARCH nên là YES cho Debug. Nếu không thì bạn đang build cho những kiến trúc mà
bạn không chạy.
ENABLE_PREVIEWS — đáng biết rằng preview build thêm các biến thể, và tắt nó trong một scheme
bạn dùng cho CI hay đo hiệu năng sẽ cắt bớt thời gian.
Còn gì giúp được nữa
Giảm @_exported import. Những cái này khiến mọi thứ phía dưới phụ thuộc vào mọi thứ phía trên,
thứ vô hiệu hóa việc biên dịch tăng dần qua ranh giới module.
Dùng private và fileprivate tường minh. Ngoài lập luận về tính dễ đọc, nó cho phép trình biên
dịch bỏ qua bớt công việc khi xét xem thứ gì có thể đã đổi. Đánh dấu class là final cũng giúp vì lý
do tương tự.
Xóa code chết. Hiển nhiên và dễ trì hoãn. Khoảng 4.000 dòng trong trường hợp của tôi, và trình biên dịch đang kiểm tra kiểu cho tất cả.
Kết quả
| Trước | Sau | |
|---|---|---|
| Build sạch | 4m 10s | 1m 20s |
| Tăng dần (một view) | 90s | 12s |
| Tăng dần (một mô hình) | 90s | 25s |
Đóng góp lớn nhất là phần bộ kiểm tra kiểu — khoảng một nửa tổng mức cải thiện, từ một buổi sáng ngồi thêm chú thích kiểu cho chừng bốn mươi biểu thức.
Nếu bạn chỉ làm một việc từ bài này: hãy bật hai cờ cảnh báo lên và nhìn xem cái gì hiện ra. Mất năm phút, và cái danh sách ấy sẽ làm bạn ngạc nhiên.