Cách thức hoạt động của tính năng Tối ưu hoá theo hướng dẫn của hồ sơ (PGO)

Tối ưu hoá theo hướng dẫn của hồ sơ (còn gọi là PGO hay "pogo") là một cách để tối ưu hoá hơn nữa các bản dựng được tối ưu hoá của trò chơi bằng cách sử dụng thông tin về cách thức hoạt động của trò chơi khi chơi trong thế giới thực. Bằng cách này, mã chạy không thường xuyên như lỗi hoặc các tình huống hiếm gặp sẽ được chỉnh giảm từ các đường dẫn thực thi quan trọng của mã, giúp tăng tốc độ chạy mã.

Sơ đồ cho thấy hình ảnh tổng quan về cách hoạt động của PGO

Hình 1. Tổng quan về cách hoạt động của PGO.

Để sử dụng PGO, trước tiên, bạn phải đo lường bản dựng để tạo dữ liệu hồ sơ mà trình biên dịch có thể làm việc. Sau đó, bạn sẽ thực thi mã bằng cách chạy bản dựng đó và tạo một hoặc nhiều tệp dữ liệu hồ sơ. Cuối cùng, bạn sao chép các tệp đó trở lại từ thiết bị rồi dùng các tệp đó với trình biên dịch để tối ưu hoá tệp thực thi bằng cách sử dụng thông tin hồ sơ bạn đã thu thập.

Cách thức hoạt động của các bản dựng được tối ưu hoá mà không cần PGO

Một bản dựng được tối ưu hoá mà không dùng dữ liệu hồ sơ sẽ sử dụng một số phương pháp phỏng đoán khi quyết định cách tạo mã được tối ưu hoá.

Một số bản dựng được nhà phát triển báo hiệu rõ ràng – ví dụ: trong C++ 20 trở lên, báo hiệu bằng cách sử dụng các gợi ý về hướng nhánh như [[likely]][[unlikely]]. Một ví dụ khác là báo hiệu bằng cách sử dụng từ khoá inline hoặc thậm chí là __forceinline (mặc dù nhìn chung, việc sử dụng cách cũ sẽ tốt hơn và linh hoạt hơn). Theo mặc định, một số trình biên dịch giả định rằng chân đầu tiên của một nhánh (tức là câu lệnh if, không phải là phần else) là chân có nhiều khả năng nhất. Trình tối ưu hoá cũng có thể đưa ra các giả định từ việc phân tích tĩnh đối với mã về cách thực thi mã, nhưng điều này thường bị giới hạn trong phạm vi.

Vấn đề với các phương pháp phỏng đoán này chính là chúng không thể trợ giúp chính xác trình biên dịch trong mọi trường hợp – ngay cả với phần đánh dấu thủ công đầy đủ – vì vậy, mặc dù mã được tạo thường được tối ưu hoá tốt nhưng sẽ không tốt bằng mọi khi nếu trình biên dịch có thêm thông tin về hành vi của mã này trong thời gian chạy.

Tạo một hồ sơ

Khi tệp thực thi của bạn được tạo với PGO bật ở chế độ được đo lường, tệp thực thi này sẽ được tăng cường bằng mã ở đầu mỗi khối mã – ví dụ: đầu hàm hoặc đầu mỗi cánh tay của nhánh. Mã này dùng để theo dõi số lượng mỗi lần khối được nhập bằng cách chạy mã mà trình biên dịch có thể sử dụng sau này để tạo mã được tối ưu hoá.

Một số hoạt động theo dõi khác cũng được thực hiện –ví dụ: kích thước của các thao tác sao chép thông thường trong một khối, để các phiên bản cùng dòng nhanh chóng của thao tác có thể được tạo sau này.

Sau khi một loại công việc đại diện nào đó được trò chơi thực hiện, tệp thực thi phải gọi một hàm – __llvm_profile_write_file() – để ghi dữ liệu hồ sơ vào một vị trí có thể tuỳ chỉnh trên thiết bị. Hàm này tự động liên kết với trò chơi của bạn khi cấu hình bản dựng đã bật tính năng đo lường PGO.

Sau đó, tệp dữ liệu hồ sơ đã viết sẽ được sao chép trở lại máy tính lưu trữ và tốt nhất nên giữ ở một vị trí cùng với các hồ sơ khác của cùng một bản dựng để có thể sử dụng cùng nhau.

Ví dụ: bạn có thể sửa đổi mã trò chơi để gọi __llvm_profile_write_file() khi cảnh hiện tại của trò chơi kết thúc. Sau đó, để lấy hồ sơ, bạn sẽ xây dựng trò chơi khi đã bật tính năng đo lường rồi triển khai trò chơi trên thiết bị Android. Khi đang chạy, dữ liệu hồ sơ sẽ tự động được ghi lại – kỹ sư đảm bảo chất lượng của bạn sẽ chạy qua trò chơi, thực hiện nhiều tình huống khác nhau (hoặc chỉ đơn giản là vượt qua bài kiểm thử thông thường).

Sau khi thực hiện xong các phần khác nhau của trò chơi, bạn có thể quay lại trình đơn chính. Thao tác này sẽ kết thúc cảnh hiện tại của trò chơi và ghi dữ liệu hồ sơ.

Sau đó, một tập lệnh có thể được dùng để sao chép dữ liệu hồ sơ từ thiết bị kiểm thử và tải dữ liệu hồ sơ lên kho lưu trữ trung tâm. Tại đây, dữ liệu được ghi lại để sử dụng sau này.

Hợp nhất dữ liệu hồ sơ

Sau khi lấy cấu hình từ một thiết bị, cấu hình đó cần được chuyển đổi từ tệp dữ liệu hồ sơ do bản dựng đo lường tạo ra, thành dạng mà trình biên dịch có thể sử dụng. AGDE sẽ tự động thực hiện việc này cho bạn, đối với mọi tệp dữ liệu hồ sơ mà bạn thêm vào dự án.

PGO được thiết kế để kết hợp kết quả của nhiều lần chạy hồ sơ được đo lường cùng nhau – AGDE cũng tự động thực hiện việc này cho bạn nếu bạn có nhiều tệp trong một dự án.

Ví dụ về lợi ích của việc hợp nhất các tập dữ liệu hồ sơ, giả sử bạn có một phòng thí nghiệm gồm nhiều kỹ sư đảm bảo chất lượng, tất cả đều chơi nhiều cấp độ trong trò chơi của bạn. Mỗi lượt chơi của họ sẽ được ghi lại, sau đó được dùng để tạo dữ liệu hồ sơ từ bản dựng được đo lường bằng PGO của trò chơi. Việc hợp nhất hồ sơ cho phép bạn kết hợp kết quả của tất cả các lần chạy kiểm thử này (có thể thực thi nhiều phần khác nhau trong mã của bạn) để cho ra kết quả chính xác hơn.

Hơn nữa, khi thực hiện kiểm thử theo chiều dọc, trong đó bạn giữ lại bản sao của dữ liệu hồ sơ giữa các bản phát hành nội bộ, thì việc tạo lại không nhất thiết sẽ vô hiệu hoá dữ liệu hồ sơ cũ. Trong hầu hết trường hợp, mã tương đối ổn định giữa các bản phát hành, vì vậy, dữ liệu hồ sơ từ các bản dựng cũ vẫn có thể hữu ích và không bị lỗi thời ngay lập tức.

Tạo bản dựng được tối ưu hoá theo hướng dẫn của hồ sơ

Sau khi thêm dữ liệu hồ sơ vào dự án, bạn có thể dùng dữ liệu này để xây dựng tệp thực thi bằng cách bật PGO ở chế độ Tối ưu hoá trong cấu hình bản dựng.

Việc này sẽ hướng dẫn trình tối ưu hoá của trình biên dịch sử dụng dữ liệu hồ sơ mà bạn đã ghi lại trước đó khi đưa ra các quyết định về tối ưu hoá.

Thời điểm nên sử dụng tính năng Tối ưu hoá theo hướng dẫn của hồ sơ

PGO không phải là tính năng bạn bật khi bắt đầu quá trình phát triển hoặc trong thời gian lặp lại mã hằng ngày. Trong quá trình phát triển, bạn nên tập trung vào phương thức tối ưu hoá dựa trên thuật toán và bố cục dữ liệu vì chúng sẽ mang lại cho bạn nhiều lợi ích hơn.

PGO xuất hiện sau đó trong quá trình phát triển, khi bạn đang hoàn thiện bản phát hành. Hãy coi tính năng Tối ưu hoá theo hướng dẫn của hồ sơ là cấu trúc chính ở trên cùng, cho phép bạn tận dụng hiệu suất sau cùng của mã sau khi bạn dành thời gian tự tối ưu hoá mã.

Cải thiện hiệu suất dự kiến với PGO

Điều này phụ thuộc vào nhiều yếu tố, bao gồm cả mức độ hoàn chỉnh và lỗi thời của hồ sơ cũng như mức độ tối ưu của mã với bản dựng được tối ưu hoá theo truyền thống.

Nhìn chung, một ước tính rất thận trọng là chi phí CPU sẽ giảm xuống ~5% trong các luồng chính. Bạn có thể thấy những kết quả khác.

Chi phí đo lường

Khả năng đo lường của PGO mang tính toàn diện và mặc dù được tạo tự động, nhưng công cụ này không miễn phí. Chi phí đo lường PGO có thể khác nhau tuỳ theo cơ sở mã của bạn.

Chi phí hiệu suất của công cụ đo lường theo hướng dẫn của hồ sơ

Bạn có thể thấy tốc độ khung hình giảm với các bản dựng được đo lường. Trong một số trường hợp, tuỳ thuộc vào mức sử dụng CPU gần như 100% trong quá trình hoạt động bình thường, mức giảm này có thể lớn đến mức khiến việc chơi trò chơi thông thường trở nên khó khăn.

Hầu hết các nhà phát triển nên xây dựng chế độ phát lại bán tất định cho trò chơi của họ. Loại chức năng này cho phép nhóm đảm bảo chất lượng của bạn bắt đầu trò chơi tại một vị trí bắt đầu lặp lại đã biết trong trò chơi (chẳng hạn như trò chơi đã lưu hoặc cấp kiểm thử cụ thể), sau đó ghi lại dữ liệu đầu vào của họ. Dữ liệu đầu vào này được ghi lại từ bản thử nghiệm có thể được tìm nạp vào bản dựng được đo lường theo PGO, được phát lại và tạo dữ liệu hồ sơ trong thế giới thực bất kể khoảng thời gian xử lý khung riêng lẻ – ngay cả khi trò chơi đang chạy chậm đến mức không chơi được.

Loại chức năng này cũng có các lợi ích chính khác, chẳng hạn như tăng thêm nỗ lực của người kiểm thử: một người kiểm thử có thể ghi lại dữ liệu đầu vào của họ trên một thiết bị, sau đó có thể phát lại trên nhiều loại thiết bị để kiểm thử xác minh bản dựng (smoke testing).

Một hệ thống phát lại như thế này có thể mang lại lợi ích to lớn trên Android khi có rất nhiều biến thể thiết bị trong hệ sinh thái – và lợi ích không dừng lại ở đó: Hệ thống này cũng có thể tạo nên một phần cốt lõi của hệ thống xây dựng tích hợp liên tục, cho phép bạn thực hiện hồi quy hiệu suất qua đêm thường xuyên và kiểm thử xác minh bản dựng.

Bản ghi phải ghi lại hoạt động đầu vào của người dùng tại thời điểm thích hợp nhất trong cơ chế nhập của trò chơi (có thể không phải sự kiện màn hình cảm ứng trực tiếp, mà thay vào đó ghi lại kết quả dưới dạng lệnh). Các dữ liệu đầu vào này cũng phải có số lượng khung được đánh dấu đơn điệu trong khi chơi để trong khi phát lại, cơ chế phát lại có thể chờ khung phù hợp kích hoạt sự kiện.

Ở chế độ phát lại, trò chơi của bạn nên tránh đăng nhập trực tuyến, không hiển thị quảng cáo và phải hoạt động ở bước thời gian cố định (ở tốc độ khung hình mục tiêu). Bạn nên cân nhắc việc tắt vsync.

Không quan trọng là mọi thứ (ví dụ: hệ thống phần tử) trong trò chơi của bạn hoàn toàn có thể lặp lại một cách quyết định, nhưng các hành động tương tự sẽ mang lại hệ quả và kết quả tương tự trong trò chơi – tức là lối chơi phải giống nhau.

Chi phí bộ nhớ của công cụ đo lường theo hướng dẫn của hồ sơ

Chi phí bộ nhớ của khả năng đo lường PGO khác nhau nhiều tuỳ theo thư viện cụ thể được biên dịch. Trong các quá trình kiểm thử, chúng tôi nhận thấy mức tăng chung khoảng 2,2 lần về kích thước của tệp thực thi kiểm thử. Việc tăng kích thước này bao gồm cả mã bổ sung cần thiết để đo lường các khối mã và không gian cần thiết để lưu trữ bộ đếm. Những quá trình kiểm thử này chưa đầy đủ và trải nghiệm của bạn có thể khác.

Thời điểm cập nhật hoặc loại bỏ dữ liệu hồ sơ

Bạn nên cập nhật hồ sơ của mình mỗi khi thực hiện thay đổi lớn đối với mã (hoặc nội dung trò chơi).

Ý nghĩa của điều này phụ thuộc chính xác vào môi trường tạo bản dựng và nơi bạn đang phát triển.

Như đã đề cập trước đó, bạn không nên mang theo dữ liệu hồ sơ trong các thay đổi lớn về môi trường tạo bản dựng; mặc dù điều này sẽ không ngăn bạn xây dựng hoặc phá vỡ bản dựng, nhưng sẽ làm giảm lợi ích về hiệu suất khi sử dụng PGO vì rất ít dữ liệu hồ sơ sẽ được áp dụng cho môi trường tạo bản dựng mới. Tuy nhiên, đây không phải là trường hợp duy nhất mà dữ liệu hồ sơ của bạn có thể trở thành lỗi thời.

Hãy bắt đầu bằng cách giả định rằng bạn sẽ không sử dụng PGO cho đến lúc sắp kết thúc quá trình phát triển khi bạn chuẩn bị cho bản phát hành, ngoài việc có thể thu thập bản ghi hằng tuần để các kỹ sư tập trung vào hiệu suất có thể xác minh rằng không có vấn đề không mong muốn nào xảy ra khi gần phát hành.

Điều này sẽ thay đổi khi bạn đến giai đoạn phát hành, khi nhóm đảm bảo chất lượng của bạn đang kiểm thử mỗi ngày và chạy qua toàn bộ trò chơi. Trong giai đoạn này, bạn có thể tạo hồ sơ từ dữ liệu đó hằng ngày và sử dụng các hồ sơ đó để định hướng cho các bản dựng trong tương lai nhằm kiểm thử hiệu suất và điều chỉnh ngân sách hiệu suất của riêng bạn.

Khi chuẩn bị cho một bản phát hành, bạn nên khoá phiên bản bản dựng mà bạn dự định phát hành, sau đó chạy quy trình đảm bảo chất lượng thông qua phiên bản đó để tạo dữ liệu hồ sơ mới. Sau đó, bạn tạo bản dựng bằng cách sử dụng dữ liệu này để tạo phiên bản thực thi cuối cùng.

Sau đó, quy trình đảm bảo chất lượng có thể chạy thử lần cuối bản dựng vận chuyển được tối ưu hoá này để đảm bảo rằng bản dựng đã sẵn sàng phát hành.