Xác định Jank liên quan đến Jitter

Jitter là hành vi ngẫu nhiên của hệ thống ngăn cản hoạt động có thể nhận biết được. Trang này mô tả cách xác định và giải quyết các vấn đề giật liên quan đến jitter.

Độ trễ của bộ lập lịch luồng ứng dụng

Độ trễ của bộ lập lịch là triệu chứng rõ ràng nhất của jitter: Một quy trình đáng lẽ phải chạy lại có thể chạy được nhưng không chạy trong một khoảng thời gian đáng kể. Tầm quan trọng của sự chậm trễ thay đổi tùy theo ngữ cảnh. Ví dụ:

  • Một chuỗi trợ giúp ngẫu nhiên trong ứng dụng có thể bị trì hoãn trong nhiều mili giây mà không gặp sự cố.
  • Chuỗi giao diện người dùng của ứng dụng có thể chịu được độ giật 1-2 mili giây.
  • Các luồng trình điều khiển chạy dưới dạng SCHED_FIFO có thể gây ra sự cố nếu chúng có thể chạy được trong 500us trước khi chạy.

Thời gian có thể chạy có thể được xác định trong systrace bằng thanh màu xanh lam trước đoạn đang chạy của luồng. Thời gian có thể chạy cũng có thể được xác định bằng khoảng thời gian giữa sự kiện sched_wakeup cho một luồng và sự kiện sched_switch báo hiệu sự bắt đầu thực thi luồng.

Chủ đề chạy quá dài

Các luồng giao diện người dùng ứng dụng có thể chạy được quá lâu có thể gây ra sự cố. Các luồng cấp thấp hơn có thời gian chạy dài thường có các nguyên nhân khác nhau, nhưng việc cố gắng đẩy thời gian chạy luồng UI về 0 có thể yêu cầu khắc phục một số vấn đề tương tự khiến các luồng cấp thấp hơn có thời gian chạy dài. Để giảm thiểu sự chậm trễ:

  1. Sử dụng bộ xử lý như được mô tả trong Điều tiết nhiệt .
  2. Tăng giá trị CONFIG_HZ.
    • Trong lịch sử, giá trị đã được đặt thành 100 trên nền tảng arm và arm64. Tuy nhiên, đây là một sự ngẫu nhiên của lịch sử và không phải là giá trị tốt để sử dụng cho các thiết bị tương tác. CONFIG_HZ=100 có nghĩa là thời gian chớp nhoáng dài 10 mili giây, nghĩa là quá trình cân bằng tải giữa các CPU có thể mất 20 mili giây (hai giây) mới diễn ra. Điều này có thể góp phần đáng kể vào hiện tượng giật trên hệ thống đã tải.
    • Các thiết bị gần đây (Nexus 5X, Nexus 6P, Pixel và Pixel XL) đã xuất xưởng với CONFIG_HZ=300. Điều này sẽ có chi phí điện năng không đáng kể trong khi cải thiện đáng kể thời gian chạy. Nếu bạn thấy mức tiêu thụ điện năng hoặc các vấn đề về hiệu suất tăng đáng kể sau khi thay đổi CONFIG_HZ, thì có thể một trong các trình điều khiển của bạn đang sử dụng bộ hẹn giờ dựa trên thời gian chờ thô thay vì mili giây và chuyển đổi thành thời gian chờ. Đây thường là một cách khắc phục dễ dàng (xem bản vá đã khắc phục sự cố hẹn giờ kgsl trên Nexus 5X và 6P khi chuyển đổi sang CONFIG_HZ=300).
    • Cuối cùng, chúng tôi đã thử nghiệm CONFIG_HZ=1000 trên Nexus/Pixel và nhận thấy nó mang lại hiệu suất và mức tiêu thụ điện năng giảm đáng kể do giảm chi phí RCU.

Chỉ với hai thay đổi đó, thiết bị sẽ trông đẹp hơn nhiều về thời gian chạy luồng giao diện người dùng khi tải.

Sử dụng sys.use_fifo_ui

Bạn có thể thử tăng thời gian chạy của luồng giao diện người dùng về 0 bằng cách đặt thuộc tính sys.use_fifo_ui thành 1.

Cảnh báo : Không sử dụng tùy chọn này trên các cấu hình CPU không đồng nhất trừ khi bạn có bộ lập lịch RT nhận biết dung lượng. Và tại thời điểm này, KHÔNG CÓ CÔNG CỤ LẬP LỊCH RT VẬN CHUYỂN HIỆN NAY CÓ NHẬN THỨC NĂNG LỰC . Chúng tôi đang nghiên cứu một cái cho EAS nhưng nó vẫn chưa có sẵn. Bộ lập lịch RT mặc định hoàn toàn dựa trên mức độ ưu tiên của RT và liệu CPU đã có luồng RT có mức độ ưu tiên bằng hoặc cao hơn hay chưa.

Do đó, bộ lập lịch RT mặc định sẽ vui vẻ di chuyển chuỗi giao diện người dùng chạy tương đối dài của bạn từ lõi lớn tần số cao sang lõi nhỏ ở tần số tối thiểu nếu kthread FIFO có mức ưu tiên cao hơn xuất hiện trên cùng một lõi lớn. Điều này sẽ giới thiệu hồi quy hiệu suất đáng kể . Vì tùy chọn này chưa được sử dụng trên thiết bị Android giao hàng nên nếu bạn muốn sử dụng tùy chọn này, hãy liên hệ với nhóm hiệu suất Android để giúp bạn xác thực tùy chọn đó.

Khi sys.use_fifo_ui được bật, Trình quản lý hoạt động sẽ theo dõi luồng giao diện người dùng và RenderThread (hai luồng quan trọng nhất về giao diện người dùng) của ứng dụng hàng đầu và tạo các luồng đó SCHED_FIFO thay vì SCHED_OTHER. Điều này giúp loại bỏ hiện tượng jitter khỏi UI và RenderThreads một cách hiệu quả; các dấu vết chúng tôi đã thu thập bằng tùy chọn này đã bật hiển thị thời gian có thể chạy theo thứ tự micro giây thay vì mili giây.

Tuy nhiên, do bộ cân bằng tải RT không nhận biết được dung lượng nên hiệu suất khởi động ứng dụng đã giảm 30% do luồng giao diện người dùng chịu trách nhiệm khởi động ứng dụng sẽ được chuyển từ lõi Kryo vàng 2,1 GHz sang lõi Kryo bạc 1,5 GHz. . Với bộ cân bằng tải RT nhận biết dung lượng, chúng tôi nhận thấy hiệu suất tương đương trong các hoạt động hàng loạt và giảm 10-15% thời gian khung hình ở phân vị thứ 95 và 99 trong nhiều điểm chuẩn giao diện người dùng của chúng tôi.

Gián đoạn giao thông

Vì nền tảng ARM chỉ cung cấp các ngắt tới CPU 0 theo mặc định nên chúng tôi khuyên bạn nên sử dụng bộ cân bằng IRQ (irqbalance hoặc msm_irqbalance trên nền tảng Qualcomm).

Trong quá trình phát triển Pixel, chúng tôi nhận thấy hiện tượng giật có thể trực tiếp do CPU 0 áp đảo và bị gián đoạn. Ví dụ: nếu luồng mdss_fb0 được lập lịch trên CPU 0 thì có nhiều khả năng bị giật hơn do gián đoạn được kích hoạt bởi màn hình gần như ngay lập tức trước khi quét. mdss_fb0 sẽ đang ở giữa công việc của chính nó với thời hạn rất chặt chẽ, và sau đó nó sẽ mất một khoảng thời gian cho trình xử lý ngắt MDSS. Ban đầu, chúng tôi đã cố gắng khắc phục điều này bằng cách đặt mối quan hệ CPU của luồng mdss_fb0 thành CPU 1-3 để tránh tranh chấp với gián đoạn, nhưng sau đó chúng tôi nhận ra rằng chúng tôi chưa bật msm_irqbalance. Khi bật msm_irqbalance, hiện tượng giật được cải thiện rõ rệt ngay cả khi cả ngắt mdss_fb0 và MDSS đều nằm trên cùng một CPU do giảm sự tranh chấp từ các ngắt khác.

Điều này có thể được xác định trong systrace bằng cách xem phần lịch trình cũng như phần irq. Phần lịch trình hiển thị những gì đã được lên lịch, nhưng vùng chồng chéo trong phần irq có nghĩa là một ngắt đang chạy trong thời gian đó thay vì quy trình được lên lịch thông thường. Nếu bạn thấy mất nhiều thời gian trong khi bị gián đoạn, các tùy chọn của bạn bao gồm:

  • Làm cho trình xử lý ngắt nhanh hơn.
  • Ngăn chặn sự gián đoạn xảy ra ngay từ đầu.
  • Thay đổi tần số của ngắt để lệch pha với công việc thông thường khác mà nó có thể gây trở ngại (nếu đó là ngắt thông thường).
  • Đặt trực tiếp ái lực CPU của ngắt và ngăn không cho nó được cân bằng.
  • Đặt ái lực CPU của luồng mà ngắt đang can thiệp để tránh gián đoạn.
  • Dựa vào bộ cân bằng ngắt để chuyển ngắt sang CPU ít tải hơn.

Việc đặt mối quan hệ CPU thường không được khuyến khích nhưng có thể hữu ích cho các trường hợp cụ thể. Nói chung, quá khó để dự đoán trạng thái của hệ thống đối với hầu hết các lần ngắt phổ biến, nhưng nếu bạn có một tập hợp các điều kiện rất cụ thể kích hoạt một số lần ngắt nhất định trong đó hệ thống bị hạn chế hơn bình thường (chẳng hạn như VR), thì mối quan hệ rõ ràng của CPU có thể trở thành một giải pháp tốt.

phần mềm dài

Trong khi softirq đang chạy, nó sẽ vô hiệu hóa quyền ưu tiên. softirqs cũng có thể được kích hoạt ở nhiều nơi trong kernel và có thể chạy bên trong tiến trình người dùng. Nếu có đủ hoạt động softirq, các quy trình của người dùng sẽ ngừng chạy softirq và ksoftirqd thức dậy để chạy softirq và được cân bằng tải. Thông thường, điều này là tốt. Tuy nhiên, một softirq rất dài có thể tàn phá hệ thống.


softirq hiển thị trong phần irq của dấu vết, vì vậy chúng rất dễ phát hiện xem sự cố có thể được tái tạo trong khi theo dõi hay không. Bởi vì một softirq có thể chạy trong tiến trình người dùng, nên một softirq xấu cũng có thể biểu hiện dưới dạng thời gian chạy bổ sung bên trong tiến trình người dùng mà không có lý do rõ ràng. Nếu bạn thấy điều đó, hãy kiểm tra phần irq để xem liệu softirq có phải là nguyên nhân hay không.

Trình điều khiển để quyền ưu tiên hoặc IRQ bị vô hiệu hóa quá lâu

Việc tắt tính năng ưu tiên hoặc gián đoạn trong thời gian quá dài (hàng chục mili giây) sẽ dẫn đến hiện tượng giật. Thông thường, lỗi này biểu hiện dưới dạng một luồng có thể chạy được nhưng không chạy trên một CPU cụ thể, ngay cả khi luồng có thể chạy được có mức độ ưu tiên cao hơn đáng kể (hoặc SCHED_FIFO) so với luồng khác.

Một số hướng dẫn:

  • Nếu luồng có thể chạy là SCHED_FIFO và luồng đang chạy là SCHED_OTHER thì luồng đang chạy bị vô hiệu hóa quyền ưu tiên hoặc ngắt.
  • Nếu luồng có thể chạy được có mức độ ưu tiên cao hơn đáng kể (100) so với luồng đang chạy (120), thì luồng đang chạy có thể bị vô hiệu hóa quyền ưu tiên hoặc ngắt nếu luồng có thể chạy không chạy trong vòng hai giây.
  • Nếu luồng có thể chạy và luồng đang chạy có cùng mức độ ưu tiên thì luồng đang chạy có thể bị vô hiệu hóa quyền ưu tiên hoặc ngắt nếu luồng có thể chạy không chạy trong vòng 20 mili giây.

Hãy nhớ rằng việc chạy trình xử lý ngắt sẽ ngăn bạn phục vụ các ngắt khác, điều này cũng vô hiệu hóa quyền ưu tiên.


Một tùy chọn khác để xác định các khu vực vi phạm là sử dụng công cụ theo dõi preemptirqsoff (xem Sử dụng ftrace động ). Trình theo dõi này có thể cung cấp cái nhìn sâu sắc hơn nhiều về nguyên nhân gốc rễ của một vùng không bị gián đoạn (chẳng hạn như tên hàm), nhưng đòi hỏi phải thực hiện nhiều thao tác xâm lấn hơn để kích hoạt. Mặc dù nó có thể có nhiều tác động đến hiệu suất hơn nhưng nó chắc chắn đáng để thử.

Sử dụng hàng đợi công việc không chính xác

Trình xử lý ngắt thường cần thực hiện công việc có thể chạy bên ngoài bối cảnh ngắt, cho phép công việc được chuyển sang các luồng khác nhau trong kernel. Nhà phát triển trình điều khiển có thể nhận thấy hạt nhân có chức năng tác vụ không đồng bộ trên toàn hệ thống rất thuận tiện được gọi là hàng công việc và có thể sử dụng chức năng đó cho công việc liên quan đến gián đoạn.

Tuy nhiên, hàng đợi công việc hầu như luôn là câu trả lời sai cho vấn đề này vì chúng luôn là SCHED_OTHER. Nhiều ngắt phần cứng đang ảnh hưởng tới hiệu suất và phải được chạy ngay lập tức. Hàng đợi công việc không có sự đảm bảo nào về thời điểm chúng sẽ được chạy. Mỗi khi chúng tôi thấy một hàng công việc nằm trong lộ trình quan trọng của hiệu suất, thì đó lại là nguồn gốc của hiện tượng giật không thường xuyên, bất kể thiết bị nào. Trên Pixel, với bộ xử lý hàng đầu, chúng tôi thấy rằng một hàng đợi công việc có thể bị trễ tới 7 mili giây nếu thiết bị đang tải, tùy thuộc vào hoạt động của bộ lập lịch và những thứ khác đang chạy trên hệ thống.

Thay vì một hàng công việc, các trình điều khiển cần xử lý công việc giống như ngắt bên trong một luồng riêng biệt nên tạo kthread SCHED_FIFO của riêng chúng. Để được trợ giúp thực hiện việc này với các hàm kthread_work, hãy tham khảo bản vá này.

Tranh chấp khóa khung

Tranh chấp khóa khung có thể là nguyên nhân gây giật hoặc các vấn đề về hiệu suất khác. Nguyên nhân thường là do khóa Hoạt động quản lý dịch vụ nhưng cũng có thể thấy ở các khóa khác. Ví dụ: khóa PowerManagerService có thể ảnh hưởng đến hiệu suất của màn hình. Nếu bạn thấy điều này trên thiết bị của mình thì không có cách khắc phục nào tốt vì nó chỉ có thể được cải thiện thông qua cải tiến kiến ​​trúc của khung. Tuy nhiên, nếu bạn đang sửa đổi mã chạy bên trong system_server, điều quan trọng là tránh giữ khóa trong thời gian dài, đặc biệt là khóa Hoạt động quản lý dịch vụ.

Tranh chấp khóa Binder

Trong lịch sử, chất kết dính có một khóa chung duy nhất. Nếu luồng đang chạy giao dịch liên kết được ưu tiên trong khi giữ khóa thì không có luồng nào khác có thể thực hiện giao dịch liên kết cho đến khi luồng gốc giải phóng khóa. Thật tệ; Sự tranh chấp chất kết dính có thể chặn mọi thứ trong hệ thống, bao gồm cả việc gửi các bản cập nhật giao diện người dùng tới màn hình (các luồng giao diện người dùng giao tiếp với SurfaceFlinger thông qua chất kết dính).

Android 6.0 bao gồm một số bản vá để cải thiện hành vi này bằng cách vô hiệu hóa quyền ưu tiên trong khi giữ khóa liên kết. Điều này chỉ an toàn vì khóa liên kết phải được giữ trong vài micro giây trong thời gian chạy thực tế. Điều này đã cải thiện đáng kể hiệu suất trong các tình huống không được kiểm soát và ngăn chặn sự tranh chấp bằng cách ngăn chặn hầu hết các chuyển đổi bộ lập lịch trong khi khóa liên kết được giữ. Tuy nhiên, không thể tắt quyền ưu tiên trong toàn bộ thời gian chạy giữ khóa liên kết, nghĩa là quyền ưu tiên đã được bật cho các chức năng có thể ngủ (chẳng hạn như copy_from_user), điều này có thể gây ra quyền ưu tiên tương tự như trường hợp ban đầu. Khi chúng tôi gửi các bản vá lên thượng nguồn, họ nhanh chóng nói với chúng tôi rằng đây là ý tưởng tồi tệ nhất trong lịch sử. (Chúng tôi đồng ý với họ, nhưng chúng tôi cũng không thể tranh cãi về hiệu quả của các bản vá trong việc ngăn chặn hiện tượng giật.)

tranh chấp fd trong một tiến trình

Điều này là hiếm. Hiện tượng giật của bạn có thể không phải do điều này gây ra.

Điều đó có nghĩa là, nếu bạn có nhiều luồng trong một quá trình ghi cùng một fd, thì có thể thấy sự tranh chấp trên fd này, tuy nhiên, lần duy nhất chúng tôi thấy điều này trong quá trình triển khai Pixel là trong quá trình thử nghiệm trong đó các luồng có mức độ ưu tiên thấp cố gắng chiếm toàn bộ CPU thời gian trong khi một luồng có mức độ ưu tiên cao đang chạy trong cùng một tiến trình. Tất cả các luồng đang ghi vào dấu vết fd và luồng có mức độ ưu tiên cao có thể bị chặn trên dấu vết fd nếu một luồng có mức độ ưu tiên thấp đang giữ khóa fd và sau đó được ưu tiên. Khi tính năng theo dõi bị vô hiệu hóa khỏi các luồng có mức độ ưu tiên thấp, không có vấn đề về hiệu suất.

Chúng tôi không thể tái tạo điều này trong bất kỳ tình huống nào khác, nhưng cần chỉ ra đây là nguyên nhân tiềm ẩn gây ra các vấn đề về hiệu suất trong khi theo dõi.

Chuyển đổi nhàn rỗi CPU không cần thiết

Khi xử lý IPC, đặc biệt là các quy trình đa quy trình, người ta thường thấy các biến thể trong hành vi thời gian chạy sau:

  1. Chủ đề A chạy trên CPU 1.
  2. Chủ đề A đánh thức chủ đề B.
  3. Thread B bắt đầu chạy trên CPU 2.
  4. Thread A ngay lập tức đi ngủ và được đánh thức bởi thread B khi thread B hoàn thành công việc hiện tại.

Nguồn chi phí chung nằm giữa bước 2 và 3. Nếu CPU 2 không hoạt động, nó phải được đưa trở lại trạng thái hoạt động trước khi luồng B có thể chạy. Tùy thuộc vào SOC và mức độ không hoạt động, thời gian này có thể là hàng chục micro giây trước khi luồng B bắt đầu chạy. Nếu thời gian chạy thực tế của mỗi bên của IPC đủ gần với chi phí chung thì hiệu suất tổng thể của đường dẫn đó có thể bị giảm đáng kể do quá trình chuyển đổi nhàn rỗi của CPU. Nơi Android thường gặp phải vấn đề này nhất là xung quanh các giao dịch liên kết và nhiều dịch vụ sử dụng liên kết cuối cùng trông giống như tình huống được mô tả ở trên.

Trước tiên, hãy sử dụng hàm wake_up_interruptible_sync() trong trình điều khiển hạt nhân của bạn và hỗ trợ chức năng này từ bất kỳ bộ lập lịch tùy chỉnh nào. Hãy coi đây là một yêu cầu chứ không phải là một gợi ý. Binder ngày nay sử dụng tính năng này và nó giúp ích rất nhiều cho các giao dịch liên kết đồng bộ, tránh các chuyển đổi nhàn rỗi của CPU không cần thiết.

Thứ hai, hãy đảm bảo thời gian chuyển tiếp cpuidle của bạn là thực tế và bộ điều chỉnh cpuidle đang tính đến những điều này một cách chính xác. Nếu SOC của bạn liên tục chuyển sang trạng thái không hoạt động sâu nhất, bạn sẽ không tiết kiệm được năng lượng bằng cách chuyển sang trạng thái không hoạt động sâu nhất.

Ghi nhật ký

Việc ghi nhật ký không miễn phí đối với chu kỳ CPU hoặc bộ nhớ, vì vậy đừng spam bộ đệm nhật ký. Chu kỳ chi phí ghi nhật ký trong ứng dụng của bạn (trực tiếp) và trong trình nền nhật ký. Xóa mọi nhật ký gỡ lỗi trước khi vận chuyển thiết bị của bạn.

Các vấn đề vào/ra

Hoạt động I/O là nguồn phổ biến của jitter. Nếu một luồng truy cập vào một tệp được ánh xạ bộ nhớ và trang đó không có trong bộ đệm trang, nó sẽ bị lỗi và đọc trang từ đĩa. Điều này chặn luồng (thường trong hơn 10 mili giây) và nếu nó xảy ra trong đường dẫn quan trọng của kết xuất giao diện người dùng thì có thể dẫn đến hiện tượng giật. Có quá nhiều nguyên nhân của hoạt động I/O cần thảo luận ở đây, nhưng hãy kiểm tra các vị trí sau khi cố gắng cải thiện hoạt động I/O:

  • Dịch vụ Pinner . Đã thêm vào Android 7.0, PinnerService cho phép khung khóa một số tệp trong bộ đệm trang. Thao tác này sẽ loại bỏ bộ nhớ để sử dụng cho bất kỳ quy trình nào khác, nhưng nếu có một số tệp được biết trước là sẽ được sử dụng thường xuyên thì việc khóa các tệp đó có thể có hiệu quả.

    Trên các thiết bị Pixel và Nexus 6P chạy Android 7.0, chúng tôi đã khóa bốn tệp:
    • /system/framework/arm64/boot-framework.oat
    • /system/framework/oat/arm64/services.odex
    • /system/framework/arm64/boot.oat
    • /system/framework/arm64/boot-core-libart.oat
    Các tệp này được hầu hết các ứng dụng và system_server sử dụng liên tục, vì vậy chúng không nên bị phân trang. Đặc biệt, chúng tôi nhận thấy rằng nếu bất kỳ thứ nào trong số đó bị phân trang ra, chúng sẽ được phân trang trở lại và gây ra hiện tượng giật khi chuyển từ một ứng dụng nặng.
  • Mã hóa . Một nguyên nhân có thể khác gây ra sự cố I/O. Chúng tôi nhận thấy mã hóa nội tuyến mang lại hiệu suất tốt nhất khi so sánh với mã hóa dựa trên CPU hoặc sử dụng khối phần cứng có thể truy cập qua DMA. Quan trọng nhất, mã hóa nội tuyến giúp giảm hiện tượng biến động liên quan đến I/O, đặc biệt khi so sánh với mã hóa dựa trên CPU. Vì các lần tìm nạp vào bộ nhớ đệm trang thường nằm trong đường dẫn quan trọng của kết xuất giao diện người dùng nên tính năng mã hóa dựa trên CPU sẽ đưa thêm tải CPU vào đường dẫn quan trọng, điều này làm tăng thêm độ giật hơn là chỉ tìm nạp I/O.

    Các công cụ mã hóa phần cứng dựa trên DMA cũng gặp vấn đề tương tự, vì hạt nhân phải dành nhiều chu kỳ để quản lý công việc đó ngay cả khi công việc quan trọng khác có sẵn để chạy. Chúng tôi đặc biệt khuyến nghị mọi nhà cung cấp SOC xây dựng phần cứng mới nên hỗ trợ mã hóa nội tuyến.

Tích cực đóng gói nhiệm vụ nhỏ

Một số bộ lập lịch cung cấp hỗ trợ đóng gói các tác vụ nhỏ vào các lõi CPU đơn nhằm cố gắng giảm mức tiêu thụ điện năng bằng cách giữ nhiều CPU không hoạt động lâu hơn. Mặc dù điều này hoạt động tốt về thông lượng và mức tiêu thụ điện năng nhưng nó có thể gây ra hậu quả nghiêm trọng đối với độ trễ. Có một số luồng chạy ngắn trong đường dẫn quan trọng của kết xuất giao diện người dùng có thể được coi là nhỏ; nếu các luồng này bị trì hoãn khi chúng được di chuyển chậm sang các CPU khác, điều này sẽ gây ra hiện tượng giật. Chúng tôi khuyên bạn nên sử dụng việc đóng gói nhiệm vụ nhỏ một cách thận trọng.

Đập bộ đệm trang

Một thiết bị không có đủ bộ nhớ trống có thể đột nhiên trở nên cực kỳ chậm chạp khi thực hiện một thao tác kéo dài, chẳng hạn như mở một ứng dụng mới. Dấu vết của ứng dụng có thể cho thấy nó liên tục bị chặn trong I/O trong một lần chạy cụ thể ngay cả khi nó thường không bị chặn trong I/O. Đây thường là dấu hiệu của việc bộ đệm trang bị hỏng, đặc biệt là trên các thiết bị có ít bộ nhớ hơn.

Một cách để xác định điều này là lấy một systrace bằng cách sử dụng thẻ pagecache và cung cấp dấu vết đó cho tập lệnh tại system/extras/pagecache/pagecache.py . pagecache.py dịch các yêu cầu riêng lẻ để ánh xạ các tệp vào bộ đệm của trang thành số liệu thống kê tổng hợp cho mỗi tệp. Nếu bạn thấy rằng nhiều byte của một tệp đã được đọc hơn tổng kích thước của tệp đó trên đĩa, thì bạn chắc chắn đang gặp phải tình trạng đập bộ đệm trang.

Điều này có nghĩa là tập công việc được yêu cầu bởi khối lượng công việc của bạn (thường là một ứng dụng duy nhất cộng với system_server) lớn hơn dung lượng bộ nhớ có sẵn cho bộ nhớ đệm trang trên thiết bị của bạn. Kết quả là, khi một phần của khối lượng công việc nhận được dữ liệu cần thiết trong bộ nhớ đệm của trang, một phần khác sẽ được sử dụng trong tương lai gần sẽ bị loại bỏ và sẽ phải được tìm nạp lại, khiến sự cố lại xảy ra cho đến khi tải xong. đã hoàn thành. Đây là nguyên nhân cơ bản gây ra các vấn đề về hiệu suất khi thiết bị không có đủ bộ nhớ.

Không có cách nào hiệu quả để khắc phục tình trạng hỏng bộ nhớ đệm trang, nhưng có một số cách để cố gắng cải thiện vấn đề này trên một thiết bị nhất định.

  • Sử dụng ít bộ nhớ hơn trong các quy trình liên tục. Càng ít bộ nhớ được sử dụng bởi các quy trình liên tục thì càng có nhiều bộ nhớ dành cho ứng dụng và bộ đệm trang.
  • Kiểm tra các bản khắc bạn có cho thiết bị của mình để đảm bảo bạn không xóa bộ nhớ khỏi hệ điều hành một cách không cần thiết. Chúng tôi đã thấy các tình huống trong đó các bản khắc được sử dụng để gỡ lỗi vô tình bị bỏ sót trong cấu hình hạt nhân vận chuyển, tiêu tốn hàng chục megabyte bộ nhớ. Điều này có thể tạo ra sự khác biệt giữa việc nhấn và không đập bộ đệm trang, đặc biệt là trên các thiết bị có ít bộ nhớ hơn.
  • Nếu bạn thấy bộ nhớ đệm trang bị giật trong system_server trên các tệp quan trọng, hãy cân nhắc việc ghim các tệp đó. Điều này sẽ làm tăng áp lực bộ nhớ ở nơi khác, nhưng nó có thể sửa đổi hành vi đủ để tránh bị hỏng.
  • Điều chỉnh lại lowmemorykiller để cố gắng giải phóng nhiều bộ nhớ hơn. Các ngưỡng của lowmemorykiller dựa trên cả bộ nhớ trống tuyệt đối và bộ nhớ đệm của trang, do đó, việc tăng ngưỡng mà tại đó các tiến trình ở cấp độ oom_adj nhất định bị tắt có thể dẫn đến hành vi tốt hơn nhưng phải trả giá bằng việc ứng dụng nền bị ngừng hoạt động nhiều hơn.
  • Hãy thử sử dụng ZRAM. Chúng tôi sử dụng ZRAM trên Pixel, mặc dù Pixel có 4GB, vì nó có thể hữu ích với các trang bẩn hiếm khi được sử dụng.