Đo lường Jitter và Latency cơ bản trước khi tối ưu
Bước đầu tiên là thiết lập đường cơ sở (baseline) để biết độ trễ hiện tại của hệ thống chưa tối ưu. Chúng ta cần sử dụng công cụ cyclictest đi kèm với gói rt-tests để đo jitter của bộ điều hành (scheduler) và độ trễ của hệ thống.
Tại sao cần làm điều này: Nếu không có con số ban đầu, bạn không thể đánh giá được mức độ cải thiện sau khi áp dụng các thay đổi về CPU governor hay IRQ affinity. Kết quả này sẽ là thước đo cho hiệu quả của toàn bộ phần 2.
Kết quả mong đợi: Bạn sẽ nhận được một file log hoặc đầu ra trực tiếp trên terminal hiển thị giá trị max latency (độ trễ cực đại) tính bằng microsecond (µs). Giá trị này thường nằm trong khoảng 100-500µs trên hệ thống mặc định, có thể cao hơn nếu hệ thống đang quá tải.
sudo apt-get update
sudo apt-get install rt-tests -y
Cài đặt gói công cụ đo lường real-time. Sau khi cài đặt, khởi động thử nghiệm đo jitter với 2 luồng: 1 luồng sinh tín hiệu (stress) và 1 luồng đo độ trễ.
cyclictest -D2 -t1000 -p99 -i200 -n -m 2 &
sleep 5
killall cyclictest
Chạy cyclictest với các tham số: -D2 (bật debug), -t1000 (chạy 1000ms), -p99 (ưu tiên cao nhất), -i200 (khoảng cách 200µs), -m 2 (ghi log ra file cyclictest.log ở thư mục hiện tại). Chờ 5 giây để thu thập dữ liệu rồi dừng tiến trình.
cat cyclictest.log | grep -v "CPU" | awk '{print $NF}' | sort -n | tail -1
Lọc file log để lấy giá trị độ trễ lớn nhất (max latency). Bạn hãy ghi lại con số này để so sánh sau khi hoàn thành các bước tối ưu hóa.
Tắt CPU governor và cấu hình C-states để giảm độ trễ
Trên Raspberry Pi CM5, bộ điều khiển CPU mặc định (ondemand hoặc schedutil) sẽ liên tục thay đổi tần số xung nhịp để tiết kiệm năng lượng. Việc này tạo ra độ trễ không xác định khi hệ thống cần chuyển từ trạng thái "ngủ" sang "tỉnh" để xử lý AI. Chúng ta cần khóa CPU ở tốc độ cao nhất.
Tại sao cần làm điều này: Khi CPU chạy ở chế tiết kiệm năng lượng, nó có thể giảm xung nhịp xuống thấp (ví dụ 400MHz) và khi có yêu cầu tính toán AI, nó phải mất thời gian để tăng lên (ví dụ 1.8GHz). Thời gian chuyển đổi này gây ra jitter. Việc tắt governor giúp loại bỏ biến số này.
Kết quả mong đợi: CPU sẽ luôn chạy ở tần số tối đa (max frequency) và không còn trạng thái ngủ sâu (C-states) gây trễ. Độ trễ jitter sẽ giảm đáng kể và ổn định hơn.
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
Trước khi thay đổi, kiểm tra chế độ governor hiện tại. Nếu thấy ondemand hoặc schedutil, ta cần thay đổi sang performance.
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance | sudo tee $cpu
done
Áp đặt chế độ performance cho tất cả các nhân CPU trên CM5. Lệnh này ghi trực tiếp vào sysfs để thay đổi ngay lập tức mà không cần restart.
Tiếp theo, ta cần vô hiệu hóa các trạng thái C-states (C-states là các mức độ ngủ của CPU). Trên ARM64, điều này được thực hiện bằng cách ẩn các driver liên quan đến CPU idle hoặc cấu hình qua kernel parameter. Tuy nhiên, cách an toàn nhất và không cần rebuild kernel là vô hiệu hóa driver cpuidle.
echo -1 | sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
Đảm bảo tần số tối thiểu bằng tần số tối đa để CPU không bao giờ xuống thấp hơn mức này, hỗ trợ thêm cho chế độ performance.
Để ngăn CPU vào trạng thái idle sâu (C1/C2/C3...), ta sẽ sử dụng systemd để tải module cpuidle vào chế độ blacklisted hoặc sử dụng kernel parameter intel_idle.max_cstate=0 (tương tự cho ARM). Đối với CM5 chạy trên Linux tiêu chuẩn, cách hiệu quả nhất là sử dụng irqbalance hoặc cấu hình cpupower nếu có, nhưng trực tiếp nhất là vô hiệu hóa driver idle:
sudo modprobe -r cpuidle 2>/dev/null || true
Cố gắng unmount module cpuidle. Nếu không thành công (do đang được sử dụng), ta sẽ blacklist nó trong file cấu hình để nó không load lại khi khởi động.
Tạo file cấu hình blacklist để vô hiệu hóa cpuidle vĩnh viễn:
echo "blacklist cpuidle" | sudo tee /etc/modprobe.d/no-cpuidle.conf
Đường dẫn đầy đủ: /etc/modprobe.d/no-cpuidle.conf. Nội dung file này sẽ ngăn kernel tải lại module cpuidle khi boot, giữ CPU ở trạng thái active liên tục.
Verify kết quả
cpufreq-info
Sử dụng lệnh cpufreq-info để kiểm tra. Bạn phải thấy dòng current policy hiển thị governor = performance và min: 1.80 GHz, max: 1.80 GHz (hoặc tần số tối đa của CM5 của bạn). Nếu thấy ondemand hoặc tần số thay đổi, hãy chạy lại lệnh set governor.
Tối ưu hóa Scheduler Linux (PREEMPT_RT) cho CM5
Raspberry Pi CM5 thường chạy kernel tiêu chuẩn (Generic) hoặc kernel được Raspberry Pi cung cấp. Để đạt được độ trễ thực sự thấp (< 50µs), chúng ta cần kernel được vá bằng PREEMPT_RT (Real-Time Patch). Nếu bạn chưa cài đặt kernel này, bước này sẽ hướng dẫn cách chuyển đổi hoặc cấu hình các tham số scheduler sẵn có để gần nhất với hành vi RT.
Tại sao cần làm điều này: Kernel mặc định của Linux sử dụng cơ chế preempt ở mức độ thấp. Các đoạn code trong kernel (ví dụ: xử lý lock, mạng, hoặc driver) có thể chiếm giữ CPU trong thời gian dài, ngăn chặn task AI của bạn chạy. PREEMPT_RT biến các spinlock thành sleeping locks, cho phép kernel bị ngắt (preempt) bất cứ lúc nào.
Kết quả mong đợi: Nếu đã cài kernel RT, hệ thống sẽ hỗ trợ các task ưu tiên cao chạy liên tục mà không bị gián đoạn bởi kernel. Nếu chưa có, việc tối ưu các tham số hiện tại sẽ giảm thiểu độ trễ nhưng không đạt mức "hard real-time".
Giả sử bạn đang sử dụng kernel tiêu chuẩn, chúng ta sẽ cấu hình các tham số để giảm thiểu sự gián đoạn. Nếu bạn đã có kernel RT, hãy skip phần cài đặt và đi thẳng đến cấu hình rcu_nocbs.
uname -r | grep -i rt
Kiểm tra xem kernel hiện tại có chứa patch RT hay không. Nếu không thấy kết quả, bạn cần build hoặc flash một kernel RT cho CM5. Hướng dẫn này tập trung vào cấu hình tối ưu cho cả 2 trường hợp.
Để giảm tải cho CPU khi xử lý RCU (Read-Copy-Update) - một nguyên nhân gây jitter phổ biến - ta sẽ di chuyển các callback RCU sang các CPU chuyên dụng (RCU No-CBS). Điều này giúp các CPU chạy task AI không bị gián đoạn bởi việc xử lý RCU.
echo 1 | sudo tee /sys/kernel/rcu/rcu_nocbs
Tham số rcu_nocbs cho phép kernel chuyển các công việc RCU nặng nề sang một CPU khác (thường là CPU cuối cùng). Trên CM5 có 4 nhân, chúng ta sẽ dành 3 nhân cho AI và 1 nhân (CPU3) làm "housekeeping".
Cấu hình file rcu_nocbs để chỉ định rõ các CPU không cần chạy RCU callbacks:
echo "0-2" | sudo tee /sys/kernel/rcu/rcu_nocbs
Đường dẫn: /sys/kernel/rcu/rcu_nocbs. Nội dung: 0-2 (có nghĩa là CPU 0, 1, 2 sẽ không chạy RCU callbacks, công việc này chuyển sang CPU 3). Đây là bước quan trọng nhất để giảm jitter cho các task chạy trên CPU 0-2.
Tiếp theo, cấu hình rcu_sched để tách biệt các callback scheduling khỏi các CPU chính:
echo "0-2" | sudo tee /sys/kernel/rcu/rcu_sched
Tương tự, đảm bảo các task scheduling của RCU cũng được chuyển sang CPU 3.
Để làm cho cấu hình này tồn tại qua lần khởi động lại, ta cần thêm tham số vào GRUB:
sudo nano /etc/default/grub
Sửa file /etc/default/grub. Tìm dòng GRUB_CMDLINE_LINUX_DEFAULT và thêm tham số rcu_nocbs=0-2 vào trong dấu ngoặc kép. Ví dụ: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash rcu_nocbs=0-2".
sudo update-grub
sudo reboot
Áp dụng thay đổi GRUB và khởi động lại hệ thống. Sau khi boot, kernel sẽ tự động cấu hình RCU theo yêu cầu.
Verify kết quả
cat /sys/kernel/rcu/rcu_nocbs
Sau khi reboot, chạy lệnh này. Kết quả phải hiển thị 0-2 (hoặc dãy CPU bạn đã cấu hình). Nếu thấy none hoặc giá trị khác, cấu hình chưa thành công.
Cấu hình IRQ affinity để ưu tiên luồng xử lý AI
Trên hệ thống nhiều nhân, các ngắt (IRQ) từ thiết bị phần cứng (USB, Ethernet, I2C, SPI) sẽ được phân tán ngẫu nhiên đến các CPU. Nếu một ngắt quan trọng (ví dụ: cảm biến hoặc giao tiếp AI) rơi vào CPU đang chạy task AI, nó sẽ gây ra sự gián đoạn (preemption) và tăng độ trễ.
Tại sao cần làm điều này: Bằng cách ép buộc các IRQ liên quan đến AI và các thiết bị ngoại vi quan trọng chỉ chạy trên một CPU cụ thể (ví dụ CPU 3 - CPU housekeeping), ta bảo vệ các CPU còn lại (0-2) khỏi bị làm phiền bởi phần cứng. Điều này tạo ra một "vùng an toàn" cho luồng xử lý AI.
Kết quả mong đợi: Các CPU 0, 1, 2 sẽ không còn nhận được IRQ từ các thiết bị ngoại vi. Jitter đo được trên các CPU này sẽ giảm xuống mức thấp nhất, chỉ còn do chính logic phần mềm gây ra.
cat /proc/interrupts
Liệt kê tất cả các IRQ đang hoạt động. Hãy tìm các IRQ liên quan đến thiết bị cảm biến hoặc giao tiếp mà bạn đang dùng (ví dụ: i2c, spi, usb, eth0). Ghi lại số thứ tự của chúng (ví dụ: IRQ 120, IRQ 125).
Giả sử bạn muốn ép tất cả các IRQ ngoại vi vào CPU 3 (nhân thứ 4). Ta sẽ sử dụng irqbalance hoặc cấu hình thủ công. Để kiểm soát chặt chẽ nhất, ta tắt irqbalance và cấu hình thủ công.
sudo systemctl stop irqbalance
sudo systemctl disable irqbalance
Dừng và tắt dịch vụ cân bằng ngắt tự động. Dịch vụ này thường phân tán IRQ khắp nơi, gây nhiễu cho task real-time.
Thiết lập affinity cho các IRQ cụ thể. Giả sử IRQ 120 là cảm biến quan trọng. Ta ép nó vào CPU 3 (hex value 8, tương ứng binary 1000). Nếu muốn ép nhiều IRQ, lặp lại lệnh này.
echo 8 | sudo tee /proc/irq/120/smp_affinity
Đường dẫn: /proc/irq/120/smp_affinity. Nội dung: 8 (CPU 3). Nếu bạn muốn ép tất cả các IRQ ngoại vi, hãy tạo một script để lặp qua các IRQ đã tìm thấy.
Tạo script tự động để ép các IRQ quan trọng vào CPU 3:
#!/bin/bash
# File: /usr/local/bin/set_irq_affinity.sh
# CPU 3 mask: 8 (binary 1000)
CPU_MASK=8
# Danh sách các IRQ cần ưu tiên (thay đổi theo phần cứng thực tế)
# Ví dụ: IRQ cho I2C, SPI, Ethernet
IRQ_LIST="100 105 120 125 130"
for irq in $IRQ_LIST; do
if [ -f /proc/irq/$irq/smp_affinity ]; then
echo $CPU_MASK | sudo tee /proc/irq/$irq/smp_affinity > /dev/null
echo "Set IRQ $irq to CPU 3"
fi
done
Đường dẫn đầy đủ: /usr/local/bin/set_irq_affinity.sh. Nội dung file là script bash trên. Chạy script này sau khi reboot để đảm bảo affinity được thiết lập đúng.
sudo chmod +x /usr/local/bin/set_irq_affinity.sh
sudo /usr/local/bin/set_irq_affinity.sh
Cấp quyền thực thi và chạy script. Script sẽ lặp qua danh sách IRQ và ép chúng vào CPU 3.
Để đảm bảo script chạy tự động khi khởi động, thêm vào systemd:
sudo nano /etc/systemd/system/irq-affinity.service
Tạo file service mới. Nội dung hoàn chỉnh:
[Unit]
Description=Set IRQ Affinity for Real-time AI
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/set_irq_affinity.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Đường dẫn: /etc/systemd/system/irq-affinity.service. File này đảm bảo script chạy ngay sau khi mạng và các thiết bị khởi động xong.
sudo systemctl enable irq-affinity.service
sudo systemctl start irq-affinity.service
Enable và start service. Bây giờ mỗi lần boot, hệ thống sẽ tự động cấu hình IRQ affinity.
Verify kết quả
cat /proc/irq/120/smp_affinity
Kiểm tra lại một IRQ cụ thể (ví dụ 120). Kết quả phải là 8 (hoặc hex value tương ứng với CPU bạn chọn). Nếu thấy f hoặc ff, nghĩa là IRQ vẫn đang chạy trên tất cả CPU, cấu hình chưa thành công.
cat /proc/interrupts | grep "CPU3"
Liệt kê lại /proc/interrupts và nhìn vào cột CPU3. Bạn sẽ thấy số lần ngắt tăng lên đáng kể cho các IRQ đã cấu hình, trong khi các cột CPU0, CPU1, CPU2 của các IRQ đó sẽ giữ nguyên hoặc tăng rất ít. Điều này xác nhận các ngắt đã được chuyển sang CPU housekeeping.
Đo lường lại Jitter sau khi tối ưu
Bây giờ hệ thống đã được cấu hình: CPU governor ở chế độ performance, C-states bị tắt, RCU callbacks được chuyển sang CPU 3, và IRQs được ép vào CPU 3. Chúng ta cần chạy lại bài kiểm tra cyclictest trên các CPU đã được bảo vệ (0-2) để xem sự cải thiện.
Tại sao cần làm điều này: Đây là bước xác nhận cuối cùng cho Phần 2. Con số jitter mới sẽ là đầu vào quan trọng cho Phần 3 (eBPF) và Phần 4 (AI). Nếu jitter vẫn cao, cần xem lại cấu hình hoặc kiểm tra phần cứng.
Kết quả mong đợi: Giá trị max latency nên giảm từ mức ban đầu (ví dụ 200-500µs) xuống còn dưới 50µs, thậm chí dưới 20µs nếu hệ thống sạch sẽ. Độ lệch chuẩn (standard deviation) cũng sẽ giảm, cho thấy hệ thống ổn định hơn.
cyclictest -D2 -t1000 -p99 -i200 -n -m 2 -a 0,1,2 &
sleep 5
killall cyclictest
Chạy lại cyclictest với tham số -a 0,1,2 để chỉ đo trên các CPU đã được tối ưu (bỏ qua CPU 3 - CPU housekeeping). Chạy 5 giây rồi dừng.
cat cyclictest.log | grep -v "CPU" | awk '{print $NF}' | sort -n | tail -1
Lọc giá trị max latency từ log mới. So sánh con số này với con số bạn đã ghi lại ở đầu bài viết. Sự khác biệt chính là thước đo thành công của các bước cấu hình Kernel và CPU này.
Điều hướng series:
Mục lục: