Gắn tiến trình vào CPU Core: Cách dùng taskset và numactl để “vắt kiệt” hiệu năng Linux

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Tại sao bạn không nên để Linux tự quyết định CPU chạy tiến trình?

Mặc định, bộ điều phối (scheduler) của Linux cực kỳ thông minh. Nó tự động phân bổ công việc vào các CPU đang rảnh. Tuy nhiên, sự linh hoạt này đôi khi lại gây họa cho các hệ thống yêu cầu độ trễ thấp (low latency) hoặc tải nặng.

Hồi làm sysadmin cho một sàn giao dịch Fintech, mình từng đau đầu vì một lỗi lạ. CPU load chỉ khoảng 30%, nhưng latency thỉnh thoảng lại nhảy vọt từ 2ms lên tận 100ms. Sau khi soi kỹ bằng perf, mình phát hiện scheduler liên tục nhảy core (context switching). Mỗi lần tiến trình bị đẩy sang core mới, dữ liệu trong L1/L2 cache bị xóa sạch. CPU phải nạp lại dữ liệu từ RAM, khiến hiệu năng tụt dốc không phanh.

Giải pháp chính là CPU Affinity (Độ thân thiết CPU). Bằng cách dùng tasksetnumactl, bạn có thể ép một tiến trình chỉ được chạy trên một core cố định. Việc này giúp tận dụng tối đa bộ nhớ đệm (cache locality) và loại bỏ tranh chấp tài nguyên.

Cài đặt công cụ

Hầu hết các bản phân phối như Ubuntu hay CentOS đều có sẵn taskset trong gói util-linux. Riêng numactl thường phải cài thủ công vì nó phục vụ các kiến trúc server phức tạp hơn.

Cài đặt trên Ubuntu/Debian:

sudo apt update && sudo apt install numactl -y

Cài đặt trên RHEL/CentOS/AlmaLinux:

sudo yum install numactl -y

Dùng taskset để quản lý CPU Affinity cơ bản

taskset là công cụ gọn nhẹ nhất để thiết lập CPU affinity qua ID của core hoặc CPU mask.

Gắn tiến trình mới vào core chỉ định

Nếu muốn chạy một script Python nặng trên core 0 và core 1, hãy dùng tham số -c (cpu-list):

taskset -c 0,1 python3 heavy_script.py

Cố định core cho tiến trình đang chạy

Bạn không cần khởi động lại ứng dụng để áp dụng thay đổi. Chỉ cần có PID (Process ID), bạn có thể “khóa” nó vào core mong muốn. Ví dụ, ép PID 1234 chạy duy nhất trên core 2:

taskset -p -c 2 1234

Kiểm tra trạng thái hiện tại

Để biết một tiến trình đang được phép chạy trên những core nào, bạn dùng lệnh:

taskset -cp 1234
# Kết quả: pid 1234's current affinity list: 0,1

Tối ưu hệ thống đa Socket với numactl

Trên các server chạy 2 hoặc 4 CPU vật lý, khái niệm NUMA (Non-Uniform Memory Access) là yếu tố sống còn. Mỗi CPU sẽ quản lý một vùng RAM riêng (Local Memory). Nếu CPU 0 phải lấy dữ liệu từ vùng RAM của CPU 1, tốc độ sẽ chậm đi khoảng 30-50% do nghẽn băng thông giữa các socket.

numactl giúp bạn gắn tiến trình vào cả CPU core lẫn vùng RAM tương ứng để đạt tốc độ cao nhất.

Xem cấu trúc NUMA

Hãy kiểm tra xem server có bao nhiêu node trước khi cấu hình:

numactl --hardware

Lệnh này sẽ hiển thị danh sách core theo từng node và khoảng cách (distance) giữa chúng.

Chạy Database tối ưu với NUMA

Khi chạy MySQL hoặc MongoDB trên server lớn, hãy ép chúng dùng tài nguyên trên cùng một node để tránh latency:

numactl --cpunodebind=0 --membind=0 /usr/bin/mongod --config /etc/mongod.conf

Trong đó:

  • --cpunodebind=0: Chỉ chạy trên các core thuộc Node 0.
  • --membind=0: Chỉ lấy RAM từ Node 0.

Nếu lo ngại Node 0 hết RAM gây crash, hãy dùng --preferred=0. Hệ thống sẽ ưu tiên Node 0 nhưng vẫn cho phép mượn RAM từ node khác khi cần thiết.

Cách theo dõi tiến trình trong thực tế

Đừng chỉ gõ lệnh rồi để đó. Bạn cần kiểm tra xem tiến trình có thực sự đứng yên tại core đã chỉ định hay không.

Sử dụng htop

Mở htop, nhấn F2 (Setup), chọn Columns. Tìm mục PROCESSOR và nhấn F5 để thêm vào bảng theo dõi. Bạn sẽ thấy ngay ID core mà mỗi tiến trình đang chiếm giữ.

Sử dụng lệnh ps

Kiểm tra nhanh bằng lệnh ps với cột psr:

ps -o pid,psr,comm -p 1234

Theo dõi thời gian thực

Để quan sát sự dịch chuyển của tiến trình mỗi giây, hãy dùng watch:

watch -n 1 "ps -o pid,psr,comm -p 1234"

Kinh nghiệm “xương máu” khi triển khai

Sau nhiều lần tối ưu hệ thống, mình rút ra 3 lưu ý quan trọng:

  1. Tránh CPU 0: Đây thường là nơi xử lý các ngắt phần cứng (interrupts) như card mạng hay ổ cứng. Ép ứng dụng nặng vào core 0 dễ gây hiện tượng nghẽn cổ chai hệ thống.
  2. Kết hợp isolcpus: Để ứng dụng độc chiếm core hoàn toàn, hãy thêm tham số isolcpus vào cấu hình Grub. Điều này ngăn scheduler đẩy bất kỳ tiến trình nào khác vào core đó.
  3. Hiểu rõ Hyper-threading: Core 0 và Core 1 có thể chung một nhân vật lý. Nếu muốn tăng tốc thực sự, hãy chọn các core thuộc các nhân vật lý (physical cores) khác nhau.

Làm chủ CPU Affinity giúp ứng dụng ổn định và chuyên nghiệp hơn hẳn. Anh em nên thử áp dụng ngay cho Redis, Nginx hoặc các worker xử lý dữ liệu để thấy sự khác biệt về tốc độ.

Share: