Tôi đang làm việc trên một ứng dụng Java để giải quyết một lớp các vấn đề tối ưu hóa số - chính xác hơn là các vấn đề lập trình tuyến tính quy mô lớn. Một vấn đề duy nhất có thể được chia thành các bài toán con nhỏ hơn có thể giải quyết song song. Vì có nhiều bài toán con hơn lõi CPU, tôi sử dụng ExecutorService và định nghĩa mỗi bài toán con là một Callable được gửi đến ExecutorService. Việc giải một bài toán con đòi hỏi phải gọi một thư viện riêng - một trình giải lập trình tuyến tính trong trường hợp này.
Vấn đề
Tôi có thể chạy ứng dụng trên Unix và trên các hệ thống Windows với tối đa 44 lõi vật lý và bộ nhớ lên tới 256g, nhưng thời gian tính toán trên Windows là một mức độ lớn hơn so với Linux đối với các vấn đề lớn. Windows không chỉ đòi hỏi nhiều bộ nhớ hơn mà việc sử dụng CPU theo thời gian giảm từ 25% lúc đầu xuống còn 5% sau vài giờ. Dưới đây là ảnh chụp màn hình của trình quản lý tác vụ trong Windows:
Quan sát
- Thời gian giải pháp cho các trường hợp lớn của phạm vi vấn đề tổng thể từ vài giờ đến vài ngày và tiêu tốn tới 32g bộ nhớ (trên Unix). Thời gian giải pháp cho một bài toán con nằm trong phạm vi ms.
- Tôi không gặp phải vấn đề này trên các vấn đề nhỏ mà chỉ mất vài phút để giải quyết.
- Linux sử dụng cả hai ổ cắm bên ngoài, trong khi Windows yêu cầu tôi kích hoạt rõ ràng bộ nhớ xen kẽ trong BIOS để ứng dụng sử dụng cả hai lõi. Dù không, tôi không làm điều này không ảnh hưởng đến việc sử dụng CPU nói chung theo thời gian.
- Khi tôi nhìn vào các luồng trong VisualVM, tất cả các luồng pool đang chạy, không có chuỗi nào đang chờ hoặc khác.
- Theo VisualVM, 90% thời gian CPU dành cho một cuộc gọi chức năng gốc (giải quyết một chương trình tuyến tính nhỏ)
- Bộ sưu tập rác không phải là một vấn đề vì ứng dụng không tạo và hủy tham chiếu rất nhiều đối tượng. Ngoài ra, hầu hết bộ nhớ dường như được phân bổ ngoài heap. 4g heap là đủ trên Linux và 8g trên Windows trong trường hợp lớn nhất.
Những gì tôi đã thử
- tất cả các loại đối số JVM, XMS cao, metaspace cao, cờ UseNUMA, các GC khác.
- các JVM khác nhau (Hotspot 8, 9, 10, 11).
- các thư viện riêng khác nhau của các bộ giải lập trình tuyến tính khác nhau (CLP, Xpress, Cplex, Gurobi).
Câu hỏi
- Điều gì thúc đẩy sự khác biệt về hiệu năng giữa Linux và Windows của một ứng dụng Java đa luồng lớn, sử dụng nhiều cuộc gọi gốc?
- Có bất cứ điều gì mà tôi có thể thay đổi trong quá trình triển khai sẽ giúp Windows, chẳng hạn, tôi có nên tránh sử dụng ExecutorService nhận hàng ngàn Callables và thay vào đó làm gì không?
ForkJoinPool
là hiệu quả hơn so với lập kế hoạch thủ công.
ForkJoinPool
thay vìExecutorService
? Mức sử dụng CPU 25% là rất thấp nếu vấn đề của bạn bị ràng buộc bởi CPU.