Kiểm thử hiệu suất

Android 8.0 bao gồm các bài kiểm tra hiệu suất của binder và hwbinder về thông lượng và độ trễ. Mặc dù tồn tại nhiều kịch bản để phát hiện các vấn đề về hiệu suất có thể nhận thấy, nhưng việc chạy các kịch bản như vậy có thể tốn thời gian và thường không có kết quả cho đến khi hệ thống được tích hợp. Việc sử dụng các bài kiểm tra hiệu suất được cung cấp sẽ giúp việc kiểm tra trong quá trình phát triển trở nên dễ dàng hơn, phát hiện các sự cố nghiêm trọng sớm hơn và cải thiện trải nghiệm người dùng.

Kiểm tra hiệu suất bao gồm bốn loại sau:

  • thông lượng chất kết dính (có sẵn trong system/libhwbinder/vts/performance/Benchmark_binder.cpp )
  • độ trễ của chất kết dính (có sẵn trong frameworks/native/libs/binder/tests/schd-dbg.cpp )
  • Thông lượng hwbinder (có sẵn trong system/libhwbinder/vts/performance/Benchmark.cpp )
  • độ trễ hwbinder (có sẵn trong system/libhwbinder/vts/performance/Latency.cpp )

Về chất kết dính và hwbinder

Binder và hwbinder là cơ sở hạ tầng giao tiếp giữa các quá trình (IPC) của Android có chung trình điều khiển Linux nhưng có những khác biệt về chất sau:

Diện mạo chất kết dính hwbinder
Mục đích Cung cấp sơ đồ IPC có mục đích chung cho khung Giao tiếp với phần cứng
Tài sản Tối ưu hóa cho việc sử dụng khung Android Độ trễ thấp tối thiểu
Thay đổi chính sách lập lịch cho nền trước/nền Đúng KHÔNG
Đối số đi qua Sử dụng tuần tự hóa được hỗ trợ bởi đối tượng Parcel Sử dụng bộ đệm phân tán và tránh chi phí sao chép dữ liệu cần thiết cho việc tuần tự hóa Bưu kiện
Kế thừa ưu tiên KHÔNG Đúng

Quá trình kết dính và hwbinder

Trình hiển thị systrace hiển thị các giao dịch như sau:

Hình 1. Trực quan hóa Systrace của các quy trình kết dính.

Trong ví dụ trên:

  • Bốn (4) quy trình schd-dbg là các quy trình máy khách.
  • Bốn (4) quy trình liên kết là các quy trình máy chủ (tên bắt đầu bằng Binder và kết thúc bằng số thứ tự).
  • Một tiến trình máy khách luôn được ghép nối với một tiến trình máy chủ, được dành riêng cho máy khách của nó.
  • Tất cả các cặp tiến trình máy khách-máy chủ được lập lịch trình độc lập bởi kernel đồng thời.

Trong CPU 1, nhân hệ điều hành thực thi máy khách để đưa ra yêu cầu. Sau đó, nó sử dụng cùng một CPU bất cứ khi nào có thể để đánh thức quy trình máy chủ, xử lý yêu cầu và chuyển ngữ cảnh trở lại sau khi yêu cầu hoàn tất.

Thông lượng so với độ trễ

Trong một giao dịch hoàn hảo, trong đó quá trình máy khách và máy chủ chuyển đổi liền mạch, các bài kiểm tra thông lượng và độ trễ không tạo ra các thông báo khác nhau đáng kể. Tuy nhiên, khi nhân hệ điều hành đang xử lý yêu cầu ngắt (IRQ) từ phần cứng, chờ khóa hoặc đơn giản là chọn không xử lý tin nhắn ngay lập tức, bong bóng độ trễ có thể hình thành.

Hình 2. Bong bóng độ trễ do sự khác biệt về thông lượng và độ trễ.

Kiểm tra thông lượng tạo ra một số lượng lớn giao dịch với các kích cỡ tải trọng khác nhau, cung cấp ước tính tốt về thời gian giao dịch thông thường (trong trường hợp tốt nhất) và thông lượng tối đa mà chất kết dính có thể đạt được.

Ngược lại, kiểm tra độ trễ không thực hiện hành động nào trên tải trọng để giảm thiểu thời gian giao dịch thông thường. Chúng tôi có thể sử dụng thời gian giao dịch để ước tính chi phí liên kết, thống kê cho trường hợp xấu nhất và tính toán tỷ lệ giao dịch có độ trễ đáp ứng thời hạn cụ thể.

Xử lý các đảo ngược ưu tiên

Đảo ngược mức độ ưu tiên xảy ra khi một luồng có mức độ ưu tiên cao hơn đang chờ một luồng có mức độ ưu tiên thấp hơn một cách hợp lý. Các ứng dụng thời gian thực (RT) gặp vấn đề đảo ngược mức độ ưu tiên:

Hình 3. Đảo ngược mức độ ưu tiên trong các ứng dụng thời gian thực.

Khi sử dụng tính năng lập lịch Trình lập lịch hoàn toàn công bằng (CFS) của Linux, một luồng luôn có cơ hội chạy ngay cả khi các luồng khác có mức độ ưu tiên cao hơn. Kết quả là, các ứng dụng có lập lịch CFS xử lý việc đảo ngược mức độ ưu tiên như hành vi dự kiến ​​chứ không phải là một vấn đề. Tuy nhiên, trong trường hợp khung Android cần lập lịch RT để đảm bảo đặc quyền của các luồng có mức độ ưu tiên cao, thì việc đảo ngược mức độ ưu tiên phải được giải quyết.

Ví dụ đảo ngược mức độ ưu tiên trong một giao dịch liên kết (luồng RT bị chặn một cách hợp lý bởi các luồng CFS khác khi chờ một luồng liên kết hoạt động):

Hình 4. Đảo ngược mức độ ưu tiên, các luồng thời gian thực bị chặn.

Để tránh tắc nghẽn, bạn có thể sử dụng tính kế thừa ưu tiên để tạm thời chuyển luồng Binder lên luồng RT khi nó phục vụ yêu cầu từ máy khách RT. Hãy nhớ rằng việc lập lịch RT có nguồn lực hạn chế và cần được sử dụng cẩn thận. Trong hệ thống có n CPU, số luồng RT hiện tại tối đa cũng là n ; các luồng RT bổ sung có thể cần phải chờ (và do đó bỏ lỡ thời hạn của chúng) nếu tất cả các CPU bị các luồng RT khác chiếm giữ.

Để giải quyết tất cả các đảo ngược mức độ ưu tiên có thể có, bạn có thể sử dụng tính kế thừa ưu tiên cho cả chất kết dính và hwbinder. Tuy nhiên, vì chất kết dính được sử dụng rộng rãi trên toàn hệ thống, nên việc cho phép kế thừa ưu tiên cho các giao dịch chất kết dính có thể gửi thư rác vào hệ thống với nhiều luồng RT hơn mức nó có thể phục vụ.

Chạy thử nghiệm thông lượng

Kiểm tra thông lượng được chạy dựa trên thông lượng giao dịch chất kết dính/hwbinder. Trong một hệ thống không bị quá tải, bong bóng độ trễ rất hiếm và tác động của chúng có thể được loại bỏ miễn là số lần lặp đủ cao.

  • Kiểm tra thông lượng chất kết dính nằm trong system/libhwbinder/vts/performance/Benchmark_binder.cpp .
  • Kiểm tra thông lượng hwbinder nằm trong system/libhwbinder/vts/performance/Benchmark.cpp .

Kết quả kiểm tra

Ví dụ về kết quả kiểm tra thông lượng cho các giao dịch sử dụng các kích cỡ tải trọng khác nhau:

Benchmark                      Time          CPU           Iterations
---------------------------------------------------------------------
BM_sendVec_binderize/4         70302 ns      32820 ns      21054
BM_sendVec_binderize/8         69974 ns      32700 ns      21296
BM_sendVec_binderize/16        70079 ns      32750 ns      21365
BM_sendVec_binderize/32        69907 ns      32686 ns      21310
BM_sendVec_binderize/64        70338 ns      32810 ns      21398
BM_sendVec_binderize/128       70012 ns      32768 ns      21377
BM_sendVec_binderize/256       69836 ns      32740 ns      21329
BM_sendVec_binderize/512       69986 ns      32830 ns      21296
BM_sendVec_binderize/1024      69714 ns      32757 ns      21319
BM_sendVec_binderize/2k        75002 ns      34520 ns      20305
BM_sendVec_binderize/4k        81955 ns      39116 ns      17895
BM_sendVec_binderize/8k        95316 ns      45710 ns      15350
BM_sendVec_binderize/16k      112751 ns      54417 ns      12679
BM_sendVec_binderize/32k      146642 ns      71339 ns       9901
BM_sendVec_binderize/64k      214796 ns     104665 ns       6495
  • Thời gian cho biết độ trễ chuyến đi khứ hồi được đo theo thời gian thực.
  • CPU cho biết thời gian tích lũy khi CPU được lên lịch cho bài kiểm tra.
  • Số lần lặp cho biết số lần chức năng kiểm tra được thực hiện.

Ví dụ: đối với tải trọng 8 byte:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

… thông lượng tối đa mà chất kết dính có thể đạt được được tính như sau:

Thông lượng TỐI ĐA với tải trọng 8 byte = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s

Tùy chọn kiểm tra

Để nhận kết quả bằng .json, hãy chạy thử nghiệm với đối số --benchmark_format=json :

libhwbinder_benchmark --benchmark_format=json
{
  "context": {
    "date": "2017-05-17 08:32:47",
    "num_cpus": 4,
    "mhz_per_cpu": 19,
    "cpu_scaling_enabled": true,
    "library_build_type": "release"
  },
  "benchmarks": [
    {
      "name": "BM_sendVec_binderize/4",
      "iterations": 32342,
      "real_time": 47809,
      "cpu_time": 21906,
      "time_unit": "ns"
    },
   ….
}

Chạy kiểm tra độ trễ

Kiểm tra độ trễ đo thời gian cần thiết để khách hàng bắt đầu khởi tạo giao dịch, chuyển sang quy trình máy chủ để xử lý và nhận kết quả. Thử nghiệm cũng tìm kiếm các hành vi xấu đã biết của trình lập lịch trình có thể tác động tiêu cực đến độ trễ giao dịch, chẳng hạn như trình lập lịch trình không hỗ trợ kế thừa ưu tiên hoặc tôn trọng cờ đồng bộ hóa.

  • Kiểm tra độ trễ của chất kết dính nằm trong frameworks/native/libs/binder/tests/schd-dbg.cpp .
  • Kiểm tra độ trễ hwbinder nằm trong system/libhwbinder/vts/performance/Latency.cpp .

Kết quả kiểm tra

Kết quả (ở dạng .json) hiển thị số liệu thống kê về độ trễ trung bình/tốt nhất/tệ nhất và số thời hạn bị bỏ lỡ.

Tùy chọn kiểm tra

Kiểm tra độ trễ có các tùy chọn sau:

Yêu cầu Sự miêu tả
-i value Chỉ định số lần lặp.
-pair value Chỉ định số lượng cặp quá trình.
-deadline_us 2500 Chỉ định thời hạn trong chúng tôi.
-v Nhận đầu ra dài dòng (gỡ lỗi).
-trace Dừng dấu vết khi đến đúng thời hạn.

Các phần sau đây trình bày chi tiết từng tùy chọn, mô tả cách sử dụng và cung cấp kết quả ví dụ.

Chỉ định các lần lặp

Ví dụ với số lần lặp lớn và đầu ra dài dòng bị vô hiệu hóa:

libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
  "other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
  "other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
  "other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
  "fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}

Các kết quả kiểm tra này cho thấy như sau:

"pair":3
Tạo một cặp máy khách và máy chủ.
"iterations": 5000
Bao gồm 5000 lần lặp.
"deadline_us":2500
Hạn chót là 2500us (2,5ms); hầu hết các giao dịch dự kiến ​​sẽ đáp ứng được giá trị này.
"I": 10000
Một lần lặp thử nghiệm duy nhất bao gồm hai (2) giao dịch:
  • Một giao dịch theo mức độ ưu tiên thông thường ( CFS other )
  • Một giao dịch theo mức độ ưu tiên theo thời gian thực ( RT-fifo )
5000 lần lặp tương đương với tổng số 10000 giao dịch.
"S": 9352
9352 giao dịch được đồng bộ hóa trong cùng một CPU.
"R": 0.9352
Cho biết tỷ lệ mà máy khách và máy chủ được đồng bộ hóa với nhau trong cùng một CPU.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
Trường hợp trung bình ( avg ), tệ nhất ( wst ) và trường hợp tốt nhất ( bst ) cho tất cả các giao dịch do người gọi ưu tiên thông thường thực hiện. Hai giao dịch miss thời hạn, khiến tỷ lệ đáp ứng ( meetR ) là 0,9996.
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Tương tự như other_ms , nhưng đối với các giao dịch do khách hàng phát hành với mức độ ưu tiên rt_fifo . Có khả năng (nhưng không bắt buộc) fifo_ms có kết quả tốt hơn other_ms , với giá trị avg và giá trị wst thấp hơn và meetR cao hơn (sự khác biệt có thể còn đáng kể hơn khi tải ở chế độ nền).

Lưu ý: Tải nền có thể ảnh hưởng đến kết quả thông lượng và bộ dữ liệu other_ms trong kiểm tra độ trễ. Chỉ fifo_ms mới có thể hiển thị kết quả tương tự miễn là tải nền có mức độ ưu tiên thấp hơn RT-fifo .

Chỉ định giá trị cặp

Mỗi quy trình máy khách được ghép nối với một quy trình máy chủ dành riêng cho máy khách và mỗi cặp có thể được lên lịch độc lập với bất kỳ CPU nào. Tuy nhiên, việc di chuyển CPU sẽ không xảy ra trong quá trình giao dịch miễn là cờ SYNC được honor .

Đảm bảo hệ thống không bị quá tải! Mặc dù độ trễ cao trong hệ thống quá tải được mong đợi nhưng kết quả kiểm tra đối với hệ thống quá tải không cung cấp thông tin hữu ích. Để kiểm tra hệ thống có áp suất cao hơn, hãy sử dụng -pair #cpu-1 (hoặc -pair #cpu một cách thận trọng). Việc kiểm tra bằng cách sử dụng -pair n với n > #cpu sẽ làm quá tải hệ thống và tạo ra thông tin vô dụng.

Chỉ định giá trị thời hạn

Sau khi thử nghiệm kịch bản người dùng rộng rãi (chạy thử nghiệm độ trễ trên một sản phẩm đủ điều kiện), chúng tôi xác định rằng 2,5 mili giây là thời hạn cần đáp ứng. Đối với các ứng dụng mới có yêu cầu cao hơn (chẳng hạn như 1000 ảnh/giây), giá trị thời hạn này sẽ thay đổi.

Chỉ định đầu ra dài dòng

Sử dụng tùy chọn -v sẽ hiển thị đầu ra dài dòng. Ví dụ:

libhwbinder_latency -i 1 -v

-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0
-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0
-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99
-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
  • Chuỗi dịch vụ được tạo với mức độ ưu tiên SCHED_OTHER và chạy trong CPU:1 với pid 8674 .
  • Giao dịch đầu tiên sau đó được bắt đầu bởi fifo-caller . Để phục vụ giao dịch này, hwbinder nâng cấp mức độ ưu tiên của máy chủ ( pid: 8674 tid: 8676 ) lên 99 và cũng đánh dấu nó bằng lớp lập kế hoạch tạm thời (được in là ??? ). Sau đó, bộ lập lịch sẽ đặt tiến trình máy chủ vào CPU:0 để chạy và đồng bộ hóa nó với cùng CPU với máy khách của nó.
  • Người gọi giao dịch thứ hai có mức độ ưu tiên SCHED_OTHER . Máy chủ tự hạ cấp và phục vụ người gọi với mức độ ưu tiên SCHED_OTHER .

Sử dụng dấu vết để gỡ lỗi

Bạn có thể chỉ định tùy chọn -trace để gỡ lỗi các vấn đề về độ trễ. Khi được sử dụng, kiểm tra độ trễ sẽ dừng ghi dấu vết tại thời điểm phát hiện độ trễ xấu. Ví dụ:

atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq
libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3
deadline triggered: halt ∓ stop trace
log:/sys/kernel/debug/tracing/trace

Các thành phần sau có thể ảnh hưởng đến độ trễ:

  • Chế độ xây dựng Android . Chế độ Eng thường chậm hơn chế độ userdebug.
  • Khung . Dịch vụ khung sử dụng ioctl để cấu hình cho chất kết dính như thế nào?
  • Trình điều khiển chất kết dính . Trình điều khiển có hỗ trợ khóa chi tiết không? Nó có chứa tất cả các bản vá chuyển đổi hiệu suất không?
  • Phiên bản hạt nhân . Khả năng thời gian thực của kernel càng tốt thì kết quả càng tốt.
  • Cấu hình hạt nhân . Cấu hình kernel có chứa các cấu hình DEBUG như DEBUG_PREEMPTDEBUG_SPIN_LOCK không?
  • Bộ lập lịch hạt nhân . Hạt nhân có bộ lập lịch nhận biết năng lượng (EAS) hoặc bộ lập lịch đa xử lý không đồng nhất (HMP) không? Có bất kỳ trình điều khiển hạt nhân nào (trình điều khiển cpu-freq , trình điều khiển cpu-idle , cpu-hotplug , v.v.) có ảnh hưởng đến bộ lập lịch không?